Repository navigation
fix(todo): recover execution idempotently with route-bound lease remediation - #8310
Conversation
0829aad to
edd3123
Compare
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent, gpt-6.1-sol, OpenAI, runtime_reported, reasoning_effort=max
[P2] 默认租约诊断与同 Turn 丢响应恢复未闭合。 默认 task-lease acquire 遇到自己的重叠租约,旧版渲染 write_scope_conflict 和冲突 Todo;新回执缺少旧 renderer 读取的 cli_args,两个 backend 都报 KeyError,诊断未输出。 目标同时与自己和他人的租约重叠时,旧 JSON 列出两个持有者;当前只留 foreign_conflict_count=1,conflicts 消失。按建议释放自己的租约后,同一目标仍被另一位持有者阻挡。 同 Turn 的 --recover-execution 首次完成并提交。模拟丢失响应后逐字重试,新版返回 coordination_operation_identity_mismatch 且没有恢复指引;拿到第一次内部 claim ID 再显式重试,才能读回已提交的原完成。File 与 SQLite 相同,未发生第二次写入或扣额。
动机 / Motivation
遇到缺失 proof 或租约冲突的 Agent 和操作者需要知道执行是否已提交、应读哪条回执以及谁仍占用 scope。 同样的本地 File/SQLite 输入:已准入 Turn 漏传 claim,重复完成;另以默认和 JSON 读取自己及混合冲突。 真实 File/SQLite 中,已准入同 Turn 的普通 completion 从三次命令缩为一次;错 Turn 在领取前被拒绝。保留原 claim ID 时完成回执可重放,未新增租约 epoch 或 debit。 默认输出、混合冲突和可重复完成的证据还不完整。 逐项读完 6 个 PR 文件;没有变更 App 的已审 proof disclosure、provider 选择或状态迁移。
改动思路 / Approach
Python 先执行原 lifecycle 操作,再由 canonical owner 给出 claim-only recovery;只增加原 claim 证明重试。TypeScript lease acquire 的新 recovery.action 另行投影自身冲突。 CLI wrapper 复用 typed claim owner,正路径规模合适;新增 recovery.action 缺少默认 renderer 对应分支,冲突投影与完成 replay 消费者未接上。 显式 opt-in 层仅组合已有能力;新 lease 诊断本身已改变默认接口,应与机器与 Markdown 消费者保持兼容。
具体改动 / Changes
基线为 e2371c476805f67903dd3c338a7e2712f577c9cc。现有规范保持当前 actor、CAS、scope 和执行身份;已提交的原操作需有稳定的重放与读回路径。PR 的新操作显式选用。 基线规范
- 一次完成。 真实 File/SQLite 中,已准入同 Turn 的普通 completion 从三次命令缩为一次;错 Turn 在领取前被拒绝。保留原 claim ID 时完成回执可重放,未新增租约 epoch 或 debit。
- 默认与混合冲突。 默认 task-lease acquire 遇到自己的重叠租约,旧版渲染 write_scope_conflict 和冲突 Todo;新回执缺少旧 renderer 读取的 cli_args,两个 backend 都报 KeyError,诊断未输出。 目标同时与自己和他人的租约重叠时,旧 JSON 列出两个持有者;当前只留 foreign_conflict_count=1,conflicts 消失。按建议释放自己的租约后,同一目标仍被另一位持有者阻挡。
- 丢响应恢复。 同 Turn 的 --recover-execution 首次完成并提交。模拟丢失响应后逐字重试,新版返回 coordination_operation_identity_mismatch 且没有恢复指引;拿到第一次内部 claim ID 再显式重试,才能读回已提交的原完成。File 与 SQLite 相同,未发生第二次写入或扣额。
- 既有预算。 基线、head 的 turn plan 都触发现有预算;保留失败。
关键代码讲解
新冲突回执交给旧 Markdown renderer,而同一 PR 的完成重试将原 proof 隐藏在成功返回中。
对主干的风险 / Risks
默认 CLI 变化不受 --recover-execution 控制;混合冲突不应通过隐藏其他 holder 来缩短输出。同一 Turn 的首次成功和响应丢失后的读回分别验证。
49 项测试通过。对照独立复现默认渲染、混合冲突与丢响应;同 Turn 首次完成、显式原 proof 重放及无额外 lease/debit 通过。 Quick premerge 6 项直接检查通过,3 项选中检查中一项失败。基线与 head 都在 turn plan 增长 11432 字符触发现有预算,无 PR 新增归因;本次合并树无冲突且 DCO 齐全。 默认输出回归与可重放性回归独立于当前主干的预算失败。 远端 CI 未取样。
为 inspect_own_conflicting_lease 接入 Markdown 的显式类型分支,保留 JSON 的冲突明细;为同 Turn 已提交的自动恢复提供绑定原 claim 的只读回执或明确恢复命令,不生成新 claim。
我的整体评价 / Verdict
对 edd3123ff1c6929c15f322904fa2b03474a86b06 请求修改。保留已通过的一次完成和权限 fence;补齐冲突输出与同一完成的可恢复路径。 修复后需要在两种本地 authority 上复验 JSON/默认 Markdown、混合冲突和同 Turn 丢响应。现有 CLI 输出预算失败也需另行修复。
English verdict: REQUEST_CHANGES. The opt-in first completion works, but default lease conflict rendering crashes, mixed conflict details disappear, and an identical bound retry cannot recover the committed completion.
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
edd3123 to
45b2ef0
Compare
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent, gpt-6.1-sol, OpenAI, runtime_reported, reasoning_effort=max
Request changes conclusion
当前 head 45b2ef02356b493149422abeacaadd7cd875ea9c,PR 目标快照 fe36f31fdedd9e5ad804e73be251c473b0e60007,实际 diff 共同祖先 c63a8f9cfc023ff22f39db5cb8b734da7653e83d。
动机 / Motivation
Agent 在当前 Todo 上忘传领取回执时,原流程需要先收到拒绝,另跑一次 claim,再重试原命令。--recover-execution 把这三步收进一次调用,并保留同一 Todo 和 Turn。新增的租约冲突指引应让操作者读回并释放他刚才看到的同一条租约。两个隔离权威库存在同名任务时,当前提示会把操作者带到另一个库。 本轮是已有 Todo 和租约 CLI 的源码评审,实际安装界面另行验收。
改动思路 / Approach
Python adapter 只消费已有的 typed claim_task 恢复,并以原参数重试一次。已有 TypeScript owner 负责决定能否领取、验证 lease 和提供冲突明细。原有回执 owner 足以处理重放。冲突指引应由掌握当前 registry/runtime 的 CLI 边界绑定完整路径,再交给 JSON 和 Markdown 消费者。
具体改动 / Changes
实际 diff 共 7 个文件、+589/-7。当前 main 在这些路径没有重叠改动,合并树可构造。完整检查了 CLI 参数、执行恢复、lease 冲突投影与两组回归。前一版的冲突渲染和同轮重放问题按这次 head 重新验证。
关键代码讲解
_recover_todo_execution_once为同一完成身份派生稳定 claim ID。已有提交在原命令丢响应后可按该 proof 重放,显式传入旧 proof 或 CAS 的调用仍由既有验证拒绝。恢复 adapterexecuteCanonicalTaskLeaseAcquire保留原冲突列表,并在只有一条自己持有的冲突 lease 时提供 inspect/release 参数。混合冲突里仍列出其他 holder。冲突投影render_task_lease_markdown能渲染新增恢复类型,修复上一版 KeyError;它把可执行文件与动作参数直接拼接,丢失本次调用的 authority route。命令渲染
依据原有 local-state 写入规范,same-effect-replay 在同轮实际 CLI 对照中已实现,authority-route 在下述冲突恢复路径未满足。规范要求先解析真实目标和权限,再执行带 CAS 的写入。原写入与冲突规则
对主干的风险 / Risks
[P2] 将冲突恢复命令绑定到产生冲突的权威库。 用显式 --registry 和 --runtime-root 在 A 上获得 own-lease 冲突;工作目录的默认路由指向 B。A、B 的隔离 fixture 都有相同 Goal、Todo、owner、key 和 version。复制 Markdown 提示的 inspect/release,两个命令都走 B。File 与 SQLite 上,B 的租约被释放,A 的原租约仍有效且内容不变。显式 CAS 在 B 上也合法,所以它不能补回缺失的 registry/runtime。最小修复是从当前 CLI 调用绑定两条命令的完整 authority route,并在不匹配默认路由时验证只读和释放都指向 A。缺失路由的位置
语义与 CI 对齐
49 项当前定向测试通过。File/SQLite 的同轮原命令重放、Markdown 和混合冲突均已通过;释放自己的租约后仍能看到其他持有者。 标准 premerge 执行 6 项直接检查和 18 项选中检查,无阻塞失败。Lark 模块预算的 1 项非阻塞失败在同 base 重现,CLI 输出预算通过。远程 CI 未查询。双库只使用临时状态,按实际 CLI 复现。
我的整体评价 / Verdict
这是 justified_increment。同轮补认领和已提交结果的重放可以减少用户重复操作,当前冲突指引却可能释放另一权威库的活动租约。复用现有 typed owner 并补齐命令路由即可完成这批改动。当前 diff 无前端布局或首屏变化。实际安装界面未验证。
English verdict: REQUEST_CHANGES. Same-Turn recovery and conflict rendering work on the tested paths. The emitted inspect and release commands can target a different authority store. Preserve the originating registry and runtime.
…pair-20261110 Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
There was a problem hiding this comment.
Reviewer: model_agent, gpt-6.1-sol, OpenAI, runtime_reported, reasoning effort max
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
动机 / Motivation
在专属 worktree 恢复遗留 Todo 执行并结算原 Turn 的 Agent。 原任务已存在 claim 或自己的写租约时,恢复需要多个容易选错 authority 的命令;同 Turn 完成重放还可能换出不同 claim。现在一个显式 recover-execution 路径使用确定 operation id,旧 claim/归属保持,租约 inspect/release 指令绑定原 registry 与 runtime。
File/SQLite 的原 Turn 完成重试不会新增 claim、重复结算或扣额度;另一个有相同 Goal/Todo/key/version 的 authority 不会被恢复指令误释放。
本 PR 不自动释放外部 Agent 的租约,不跨 authority 找“同名”任务,也不绕过当前 Turn 身份和 CAS 校验。
改动思路 / Approach
恢复意图复用现有 Todo claim/settlement owner,以确定性 operation id 识别重放,显式参数才触发。租约诊断按 inspect/release 各自结构渲染,保留全部冲突而非只显示自己的那一项;nested 命令携带原始路由与 CAS。最小合法恢复必须同时解决身份、命令路由和完整冲突信息,不能只把错误改成成功。
本次只闭合既有执行的显式恢复、同 Turn 重放和租约恢复提示,不增加全目录锁、不替使用者授予释放别人租约的权限。
入口为 todo claim/complete --recover-execution 与 task-lease acquire;权威输入来自 当前 agent/Turn、原 claim、registry/runtime、lease key/version,由 现有 Todo command + TS task_lease_acquire 决定,结果交给 CLI JSON/Markdown、claim receipt、provider state。--recover-execution 是不能从旧回执臆造的显式恢复意图;operation id 从上下文确定推导,claim/lease/settlement 仍是既有权威,不新增影子状态。 相同领域的可逆精简已经在本次完成;没有把职责搬成另一套平行规则。
具体改动 / Changes
关键代码讲解
_recover_todo_execution_once:由原上下文生成确定 operation id,重复完成时恢复同一 claim,不把新建议换绑到原 Turn。 输入来自当前 agent/Turn、原 claim、registry/runtime、lease key/version,下游为CLI JSON/Markdown、claim receipt、provider state;失败由冲突保留全部 holder;本人显式释放采用原 route+CAS;外部 holder 保留阻塞处理。
render_task_lease_markdown:按动作渲染 inspect/release,避免假设所有 recovery 都有相同 cli_args。 输入来自当前 agent/Turn、原 claim、registry/runtime、lease key/version,下游为CLI JSON/Markdown、claim receipt、provider state;失败由冲突保留全部 holder;本人显式释放采用原 route+CAS;外部 holder 保留阻塞处理。
handle_task_lease_command:将 registry/runtime 前缀应用到 nested inspect 和 release,保留 owner、key、CAS 及全部冲突。 输入来自当前 agent/Turn、原 claim、registry/runtime、lease key/version,下游为CLI JSON/Markdown、claim receipt、provider state;失败由冲突保留全部 holder;本人显式释放采用原 route+CAS;外部 holder 保留阻塞处理。
规格依据:https://github.com/loopx-project/loopx/pull/8310,固定修复前版本 45b2ef02356b493149422abeacaadd7cd875ea9c。映射标签 bounded-recovery-replay:显式恢复保留原执行归属、同 Turn 幂等、完整冲突,并把恢复操作绑定原 authority。 —— implemented,验证来自公开测试入口 uv run --extra test python -m pytest -q tests/control_plane/test_task_lease_cli.py tests/control_plane/test_todo_claim_argument_recovery.py。该标签是本次评审的映射名,不伪称规格原有编号。
差异形态:production: 5 files +308/-7; tests_or_fixtures: 2 files +373/-3; docs: 0 files +0/-0; generated: 0 files +0/-0; mechanical_moves: 0 files +0/-0。省略 --recover-execution 仍采用原拒绝规则;inspect/release 是建议并要求显式执行,拥有 lease 不授权释放外部 holder。
对主干的风险 / Risks
恢复不等于给原操作重新授权;deterministic id、Turn 绑定与 current claim 必须一起验证。可复制的 CLI 建议若省略 registry 会影响另一份同名状态,因此 JSON 与 Markdown 都要覆盖。PostgreSQL 真实隔离库验证现有 identity fencing,不能据此声称所有分布式 Host 都已验证。
正向:绑定真实 quota guard 的 Todo 同 Turn 完成两次;原 claim 与 provider state 第二次不变;Markdown 与 JSON 都有合法恢复动作。 负向与恢复:环境变量故意指向同名 decoy authority;生成的 inspect/release 仍只访问原 authority,外部与自己的混合冲突均报告,decoy lease 不变。
验证:51 passed;含 File/SQLite 真实 quota→complete→retry、同名 decoy route、混合冲突和 Markdown。另隔离 PostgreSQL store 391 项、service 11 项通过,无 service skip。 Ruff/diff hygiene 与 exact-head canary direct/run checks 无失败;manual_hold_count=0。手工 benchmark/authority 判断由本次完整评审执行,不把该 hold 写成自动通过。 历史失败保留为反例,不把新 head 的通过反写成历史通过。GitHub CI 与合并策略是单独的 readiness 判断;本结论不从红绿状态推导代码正确性。
PostgreSQL 的一次失败来自复用已轮换身份的测试库,独立新库重跑 store/service 验证通过;没有修改活跃 Goal 或生产数据库。
合并读回:该精确 head 已合入,merge commit 为 3b82171ed5e90482b10cf92ac79775d783dfb6f0。合并前 readiness 为 ready=false,GitHub 检查汇总 {"failure": 1, "pending": 23, "success": 11};review conclusion 有效、线程完整且 unresolved=0。build 的已完成失败停在研究测量帮助关闭场景:旧测试把真实 opaque finding identity 写死为 finding-1。相同 packaged 场景已复现,并在 #8231 修正为比较操作前后的实际选择;目标主干包含该修复。本次没有把旧 run 的失败改写为通过。 依据维护者对本批“自修复自合并”的明确授权使用 admin bypass 处理策略/CI 等待;必需本地验证、已修复发现的核对与精确版本评审单独完成。
我的整体评价 / Verdict
最终评审版本为 30fc617158806cad5cb86800d6fe93e5f0b327e4,检查覆盖整个剩余 diff 与当前调用方,不仅是最后一条 comment。File/SQLite 的原 Turn 完成重试不会新增 claim、重复结算或扣额度;另一个有相同 Goal/Todo/key/version 的 authority 不会被恢复指令误释放。 本次只闭合既有执行的显式恢复、同 Turn 重放和租约恢复提示,不增加全目录锁、不替使用者授予释放别人租约的权限。 上述已有缺陷均有入口级反例与恢复读回,未发现仍需阻止本次交付的具体问题。
已检查 loopx/cli_commands/todo.py 及现有消费者,本 PR 不改变渲染布局或内容层级;本次验证集中于入口、状态与恢复。 没有对已安装 App、真实个人账号或付费模型作额外观测。
本 PR 不自动释放外部 Agent 的租约,不跨 authority 找“同名”任务,也不绕过当前 Turn 身份和 CAS 校验。 合并仍需该版本的远端 head、未解决线程、旧阻塞评审与 readiness 读回;本评审不自行赋予合并或付费执行权限。
English verdict: APPROVE. Head 30fc617. The bounded fixes preserve existing authority and recovery contracts; the stated untested boundaries are not claimed as verified.
维护者授权本批修复后自合并。已独立核对 30fc617158806cad5cb86800d6fe93e5f0b327e4 的全部旧发现:nested inspect/release 已绑定原 registry/runtime,并用同名 decoy authority 验证只作用原库;此前 Markdown、混合 holder 和同 Turn 完成重放也重新核对。File/SQLite 51 项、真实隔离 PostgreSQL store/service 通过,原 claim 与第二次 state 不变。保留原讨论,撤销已修复的阻塞状态,不将历史失败改为通过。
Problem and outcome
在专属 worktree 恢复遗留 Todo 执行并结算原 Turn 的 Agent。 原任务已存在 claim 或自己的写租约时,恢复需要多个容易选错 authority 的命令;同 Turn 完成重放还可能换出不同 claim。现在一个显式 recover-execution 路径使用确定 operation id,旧 claim/归属保持,租约 inspect/release 指令绑定原 registry 与 runtime。
File/SQLite 的原 Turn 完成重试不会新增 claim、重复结算或扣额度;另一个有相同 Goal/Todo/key/version 的 authority 不会被恢复指令误释放。
Design and scope
恢复意图复用现有 Todo claim/settlement owner,以确定性 operation id 识别重放,显式参数才触发。租约诊断按 inspect/release 各自结构渲染,保留全部冲突而非只显示自己的那一项;nested 命令携带原始路由与 CAS。最小合法恢复必须同时解决身份、命令路由和完整冲突信息,不能只把错误改成成功。
本次只闭合既有执行的显式恢复、同 Turn 重放和租约恢复提示,不增加全目录锁、不替使用者授予释放别人租约的权限。
本 PR 不自动释放外部 Agent 的租约,不跨 authority 找“同名”任务,也不绕过当前 Turn 身份和 CAS 校验。
Validation
51 passed;含 File/SQLite 真实 quota→complete→retry、同名 decoy route、混合冲突和 Markdown。另隔离 PostgreSQL store 391 项、service 11 项通过,无 service skip。
Reproduction:
uv run --extra test python -m pytest -q tests/control_plane/test_task_lease_cli.py tests/control_plane/test_todo_claim_argument_recovery.pyRuff/diff hygiene 与 exact-head canary direct/run checks 无失败;manual_hold_count=0。手工 benchmark/authority 判断由本次完整评审执行,不把该 hold 写成自动通过。
PostgreSQL 的一次失败来自复用已轮换身份的测试库,独立新库重跑 store/service 验证通过;没有修改活跃 Goal 或生产数据库。
Reviewed head:
30fc617158806cad5cb86800d6fe93e5f0b327e4. Historical timing claims or failures are not reused as evidence for an unmeasured final implementation.