Repository navigation
fix(todo): reconcile explicit CAS after expired claim recovery - #8225
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent — gpt-6-astra / OpenAI; runtime_reported; reasoning_effort=ultra.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
动机 / Motivation
使用显式版本检查更新 Todo 的 Agent,在执行领取过期后,需要得到能够继续原任务的恢复指引。旧指引要求重新领取后原样重试,但领取本身推进了权威版本,因此原版本必然再次被拒绝。这里的 CAS 指“只有当前版本仍等于调用者读到的版本,才允许写入”,其保护不能为了减少重试而删除。
此次修复让调用者重新领取后只读目标 Todo,核对当前内容,再用新的领取引用和读取版本重试。可观察成果是原编辑能沿返回的命令完成并读回,第三方修改仍受到保护。不自动重写版本、不新增规划轮或表单,也不证明实际模型采用或长程分数收益。
改动思路 / Approach
判定仍在共享 TypeScript claimExecutionRecovery,Python 只把指导投影成原 CLI 路由。todo_update 单独传递调用者确实提供过的版本,避免内部用于预览的当前版本被误当成调用者意图。新指引只在“同一执行已过期,且原编辑含显式 CAS”时出现;真正的领取、Stop、成员资格、写入与并发控制继续由原 owner 判断。
这是修复原恢复协议,而不是增加第二个调度器。直接替换成领取回包中的版本虽然少一次读取,却不能证明从旧读取以来没有第三方改动;即使 todo_changed=false 也只说明领取本次没改 Todo 正文。新增的是一次有范围的读取和核对,已授权的 Agent 可自行完成,无需增加人工确认。
| 原请求/后续事件 | 精确头上的恢复与结果 |
|---|---|
| 过期领取,显式 CAS | claim → targeted read → reconcile → retry |
| 不带显式 CAS 的 update/complete | 保留原 claim → retry 和完整回包 |
| 读取后出现并发编辑 | 原 CAS 拒绝;无暗中重基、无写入 |
| 新领取后来转交另一 Agent | 先拒绝过时 CAS;即使更新 CAS,旧执行引用仍被拒绝 |
具体改动 / Changes
评审基于 e41288848929faee77bdadaf91fa15dacfa152b1。独立规范是改动前的 docs/reference/handoff-mode.md,固定在 ac62962255322cd80a5b701a4187c6f47bc17d8c,特别是 Ordinary Agent CLI work 的执行引用、无隐式重新领取、原回执重放及并发版本保护;新增文档是修复说明,不能反过来为实现自证。
- CLAIM-REF:过期、替换和外来执行不能借用。已实现;领取后转交的独立 File/SQLite 负例保留新 owner,旧请求仅得到重新核对指引。
- RECOVERY-NO-EFFECT:恢复说明不授予执行权,也不写状态。已实现;原拒绝、读取、CAS 竞争后的 Todo/lease 快照不变。
- REVISION-FENCE:版本改变需要核对,不可绕过 CAS。已实现;领取前第三方改动可读回,领取后的新版本不被自动填入重试,读后竞争仍拒绝。
- RECEIPT-REPLAY:原操作回包丢失后应重放原效果。已实现;相同接受后的 retry 再调用不重复修改或续租。
关键代码讲解
executeCoordinationTodoUpdate(todo_update.ts:227)保留既有错误优先级,进入原 claim proof 的异常分支时,额外传入caller_expected_provider_revision。它没有改变 CAS 判断或提交事务,也没有把观察到的新 head 自动写回用户请求。claimExecutionRecovery(claim_execution_recovery.ts:39)基于经过验证的过期执行和真实 dry-run claim 结果,加入目标 Todo 的 inspect 步骤,并声明两个待填引用。未带 CAS 的分支原文保留;非同一执行仍走 reconcile,而不是提供可抢占的 claim/retry。_route_native_recovery(todo_event.py:66)从解析后的原请求生成命令,保留 operation id、编辑意图、registry/runtime route,然后把过期 claim id 与旧 CAS 替成明确占位符。既覆盖--flag value,也覆盖--flag=value;看起来像选项的正文仍按正文传递。- 现有真实 CLI 测试新增领取前编辑、读取后竞争、完整重试读回与回执重放。双语 handoff 文档同步说明目标读取的顺序与局限,没有新建开关、状态机或强制产品表单。
对主干的风险 / Risks
没有发现阻断问题。最重要的风险是把“新的领取成功”误解为“旧编辑仍适用”,或者在重试模板里残留旧引用。本次没有用自动重基解决冲突;恢复是指导,真正写入仍执行当前权限和 CAS。共享 owner 的变动很小,主要维护成本是保留显式 CAS 与无 CAS 两种合法调用语义,而不是增加持久版本或并行规则。
独立验证使用真实生产 CLI、TypeScript owner 和隔离的 File/SQLite,不写活跃 Goal,不调用模型。结果如下:
| 验证 | 结果与证据边界 |
|---|---|
| 现有两份相关 CLI/源边界套件 | PASS:26 项,包含 Stop、成员撤销、外来执行、重放、编辑读回 |
| 独立 ownership/CAS 组合反例 | PASS:4 项;新领取后真实 transfer,旧 CAS 与旧执行引用依次拒绝,状态不变 |
| 同一 fixture 的基线/精确头比较 | 基线显式 CAS 的两个案例因缺 inspect FAIL;精确头 PASS,证明缺陷敏感性 |
| 无 CAS 完整响应比较 | PASS:4 组 File/SQLite × update/complete;只归一临时目录并移除非确定 observation receipt,其余错误、恢复描述及命令参数完全相同 |
| Ruff、diff、双语 RFC 检查、公开边界 | PASS;另读回作者同源 quick premerge 的 6 个 direct + 4 个 selected 检查与 36 项 TS 验证 |
作者披露的更宽 CLI 套件存在主干已有的精确文本 oracle 问题:旧预期未列当前 --summary / --include-archived。保留该未修问题,不把它称为本 PR 修复,也不以它掩盖这里的必要验证。按当前审查合同没有查询或等待远端 CI。PostgreSQL 没有执行集成测试;本 PR 不改 PostgreSQL provider、事务协议或持久格式,这次证据仅资格化共享恢复语义与真实本地两个 provider,不冒称该服务端部署已验证。
语义与 CI 对齐
沿用既有 claim/recovery/CAS 词汇与 descriptor,仅扩展显式版本请求的恢复指导。新增分支的触发条件、先后顺序、目标范围、来源和权限边界已逐项核对;不从正文字符串判断 lease 状态。读取后的内容核对仍需要调用 Agent 执行,CLI 不能证明它理解了第三方修改,因此实际模型采用与节省成本仍未测量。
我的整体评价 / Verdict
这是一项完整、可回滚的共享恢复修复:它消除按旧指引必然再遇到一次 stale revision 的路径,并保留 Stop、ownership、CAS、原操作意图和回执重放。长程继续工作的协议改善已由“拒绝→领取→核对→写入→再次重放”验证;实际模型效率和效果收益仍未证明。用户体验改善在于指引可执行且只读取目标 Todo,没有额外人工授权或全 Goal 重读。
检查的是 CLI JSON/Markdown 恢复流程,未修改渲染页面,因此 visual review 不适用;未验证范围为实际模型采用、PostgreSQL 部署与安装后的集成版本。当前 PR 边界仅修复原显式 CAS 恢复;组合安装应另做接收端验证。APPROVE 不构成合并或活体更新授权。
English verdict: APPROVE at e412888. No blocking finding; real File/SQLite recovery, ownership and CAS negatives, replay, and baseline/head no-CAS parity passed. Model adoption and deployed integration remain unmeasured.
Goal And Delivered Outcome
An expired managed claim makes
todo updatereturn recovery instructions. When the caller also supplied an explicit provider revision, those instructions previously said to claim again and retry the unchanged edit: the new claim advances authority, so retaining the old revision immediately fails CAS.The shared recovery owner now requires claim → read this Todo → reconcile → retry for this case. The CLI keeps the original edit and operation id but leaves both new references as explicit placeholders. It neither automatically rebases the revision nor treats
claim.todo_changed=falseas proof against intervening edits. Calls without an explicit CAS keep their existing direct recovery, includingtodo complete.mainatac62962255322cd80a5b701a4187c6f47bc17d8c.Author Declaration
docs/reference/handoff-mode.mdat the base revision, preserving original claim ownership, expiry, explicit revision and once-only receipt contracts.claim_execution_recovery.tstodo_update.ts, shared recovery ownertodo_event.pyScope And Continuation
Complete within this scope. This repairs the existing shared Todo update recovery and JSON/Markdown CLI projection; no new capability, configuration, planner or execution authority is introduced. The same optional guidance descriptor is reused. Completion CAS is not added. Goal Stop, claim admission and provider CAS checks are unchanged.
The bounded adjacent cleanup is safe projection of pending references for both
--flag valueand--flag=value; no broader parser or lease refactor is needed. Live model adoption, latency savings and outcome improvement remain unmeasured. This PR does not change running workers or merge/install itself.Validation
e41288848929faee77bdadaf91fa15dacfa152b1(same source bytes as the immediately preceding author tests).uv run --extra test python -m pytest -q tests/control_plane/test_canonical_claim_execution_proof.py tests/control_plane/test_planned_transfer_source_boundary.py: 26 passed on real File/SQLite, covering original and fresh claim, explicit CAS, before-claim edit, after-read contention, replay, foreign ownership and existing Stop/withdrawal boundariesauthority_store.test.tsandsqlite_authority_store.test.ts: 36 passedtest_todo_list_validation_preserves_exact_unsupported_diagnosticomits current--summary/--include-archivedflags. Reproduced unchanged on base; not weakened or fixed hereFrontend / Visual Evidence
UI impact: none. This corrects an existing agent-facing CLI JSON/Markdown recovery flow and preserves the existing task-control owner. No dashboard, Lark layout, editor, new setting or viewport changes. JSON and rendered Markdown are verified through the same real CLI response; screenshots are not applicable.
Type of Change / LoopX Area
Shared-authority RFC fixture impact: no promotion or provider-routing claim; existing real File/SQLite conformance is exercised. PostgreSQL runtime is not exercised in this bounded local CLI qualification.
Boundary Checklist