Restore exact owner-confirmed Todo work without a lease deadlock - #5013
Conversation
huangruiteng
left a comment
There was a problem hiding this comment.
Request changes conclusion (author-owned PR; GitHub blocks formal self-review). Reviewed exact head 560c1da95a4872f1c1e651472ea7a21ffa20a30c.
动机
#4971 的死锁来自同一件已获 owner 确认的 Todo:Agent 持硬租约时误清除已有等待条件,释放租约后验收绑定判其 stale;stale 禁止重取租约,而普通 update 又要求活跃租约。让这类精确回退可执行,比强迫 owner 对原工作重新绑定更符合长期执行体验。
改动思路
复用 TS reviewed-update 边界,只在注册身份、claim、排除规则通过且租约证明失败之后尝试恢复。候选 Todo 的完整工作摘要必须与现有 owner 绑定一致;provider revision CAS 和 operation receipt 负责并发与重试。活跃租约、旧执行证明、附带生命周期/验证器/归属修改被拒绝。恢复不授予执行权,仍需重新取租约;比放开 stale 工作 acquire 更窄。
具体改动
todo_acceptance_restoration.ts 判断精确候选摘要及禁改字段,todo_update_admission.ts 在原 actor/claim/exclusion 入口后接入。task_lease_state.ts 在读取边界规范化历史无 schema tag 租约,todo_deferred_reopen.ts 复用它;Python frontier 改成精确恢复或 owner rebind 指引。双语 RFC、参考文档及 Python/TS 用例覆盖 File/SQLite/PostgreSQL、失败分支和原 Turn 的完成、写回、单次 spend。另有一处无关的投影 typing hunk。
对主干的风险
独立 head 上实跑真实 File/SQLite CLI 恢复/结算 2 例、含隔离 PostgreSQL 的 TS/provider 84 例(0 skipped)、TypeScript typecheck、配置 mypy 和 diff 空白检查,均通过。负例涵盖错值、外人/被排除 Agent、活跃租约、旧 CAS、额外修改和历史无 schema 的活跃租约;隔离服务器未接触活跃 Goal。
[P1 集成阻断] git merge-tree --write-tree origin/main 560c1da95a4872f1c1e651472ea7a21ffa20a30c 在 loopx/control_plane/coordination/local_authority_shadow_projection.py 报内容冲突。主干已完成该投影类型检查,PR 的伴随 hunk 重叠。请 rebase 并保留一个检查实现,在新 exact head 重跑 File/SQLite 恢复→新租约→原 Turn 结算和 PostgreSQL authority 用例。当前无法验证冲突解决后的主干结果;不是声称独立 head 的验收恢复已复现功能性错误。
我的整体评价
修复贴合原死锁,复用既有 TS 状态/效果 owner 和回执,历史租约规范化也收敛到读取边界。相关的未来维护改进是去掉重复 typing hunk,不扩大为通用历史回滚。独立 head 未见额外阻断,但集成 head 缺失,故请求修改并在 rebase 后按完整 PR 复审。
English verdict: REQUEST_CHANGES - Exact head 560c1da passes 84 File/SQLite/PostgreSQL TypeScript cases, two real CLI cases and configured static checks, but conflicts with current main in the projection typing file. Rebase and revalidate the integrated head before approval.
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
560c1da to
4d280ba
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval). Reviewed exact head 4d280ba7bb375cea1aff19955cd79f40ef14c60e, rebased onto origin/main c36b41678928df2bd64d13cf6eeb81c5372b06c5. The rebase dropped one already-upstream typing commit (fix(types): preserve narrowed authority projection readback) instead of re-applying it.
动机
已复核基线行为:Agent 可以在持有硬租约时清掉 owner 已确认 Todo 的既有 wait,随后释放租约,于是卡住——重新获取被执行权时被「stale acceptance」拒绝,而恢复又要求持有活跃租约,形成死锁。基线还暴露了一个相邻兼容问题:没有 schema 的历史 canonical 租约在共享租约规则下会被当成「不活跃」。
对长期推进而言,这个修复把「已确认的既有工作」重新变成可执行目标:修复前一次误清扫会让 Todo 无法重新获取执行权,后续 Turn 只能等待 owner 干预;修复后同一个 claimed Agent 可以在 provider-revision CAS 下恢复完全一致的先前文本/等待条件,再重新获取租约继续执行,整个过程不需要新的权限或人工历史回滚。对用户旅程而言,受影响入口是 todo update(含 managed frontier 指引)与 canonical 租约获取:失败信息从「不可能满足的泛化恢复建议」变成有界的 CAS 恢复路径与显式的 owner 回退。
改动思路
恢复入口复用既有 reviewed-update 边界:只有「同一个已 claim 的 Agent 把候选工作整份摘要恢复到既有 owner 绑定」时才允许,条件、归属、生命周期与租约血统都不变;执行仍然必须重新获取新租约。未知的先前值或更宽的改动依旧返回「需要 owner 复核/重新绑定」。另一个改动是历史兼容:在 TypeScript 读取边界把无 schema 的 canonical 租约做一次规范化(不改写持久化历史),让 deferred reopen 复用同一规范化结果,而不是在第二个位置重新解释。
语义与集成
准入顺序保持「普通 actor/exclusion 判断在前」:活跃租约、显式提供的 stale 执行证明、冲突重试以及捆绑的生命周期/validator 编辑仍然被拒绝,因此这不是任意历史回滚,也不会顺带把 acceptance 重新绑定。managed frontier 指引现在给出可执行路径,并在恢复后清掉对应的 stale hold;恢复本身不结算、不消费 Turn,被阻塞的 no-spend 结算仍归其既有 owner。
具体改动
loopx/control_plane/coordination/todo_acceptance_restoration.ts、todo_update_admission.ts、todo_deferred_reopen.ts、task_lease_state.ts:恢复准入、整份候选摘要比对、无 schema 租约的一次性规范化与 deferred reopen 复用。loopx/control_plane/goals/goal_frontier/acceptance.py:frontier 指引改为可执行的恢复路径,并修复一个既有的结果类型收窄错误(返回已检查的窄化字典)。- 测试:
tests/control_plane_ts/goal_acceptance_authority.test.ts(+70 行)与tests/control_plane_ts/todo_update.test.ts(+13 行)覆盖恢复、排除、活跃租约、stale CAS 与重放;tests/control_plane/test_acceptance_restoration.py覆盖完整的 managed Turn 恢复案例。 - 文档:双语 ledger
2026-09-24-exact-acceptance-restoration.md与docs/reference/goal-acceptance-observations.md记录边界(这不是任意回滚,也不改变 provider 默认值或 D1–D3)。
对主干的风险
第一是语义边界容易被误读为「通用回滚」:本 PR 只接受整份候选摘要与既有 owner 绑定完全一致的情形,其他情况仍要求 owner 复核;文档与拒绝信息都写明了这一点。第二是历史租约解释:规范化只发生在读取边界且不改写历史,但这意味着「无 schema 的旧租约」从此被视为可解释;我用真实 PostgreSQL 16 隔离库跑完了整个 authority-store 集成套件来覆盖这一点(293 个用例、0 失败、0 跳过)。第三是继承红灯:canary premerge --goal-id loopx-meta 选中 19 项检查、18 项通过,唯一失败的 semantic-vocabulary-drift-smoke 是模块对预算 44 > 43,我在干净的 origin/main 上复现同一失败;同时 tests/control_plane/test_canonical_lease_renew.py 有 6 个 invalid_mode/malformed_yaml 用例失败,同样的 6 个失败在干净的 origin/main c36b41678 上原样复现,因此也是继承问题而非本 diff 引入。本目标不等待远端 CI,打包前端的浏览器行程未在本地复跑(PR 正文披露了其继承失败与基线对照)。
我的整体评价
没有发现阻断项。这个增量用最小、可逆、带 CAS 的方式解开了「owner 已确认工作无法重新获取执行权」的死锁,同时没有把它扩写成通用历史回滚,也没有放宽租约或 acceptance 权威;历史无 schema 租约的解释修正在读取边界一次完成,真实 PostgreSQL 套件全绿。验证覆盖正例、负例与真实路径:2 个 strengthened managed-Turn 用例、84 个 TS 验收/更新用例(15 个声明跳过)、3084 个 control-plane TS 用例(0 失败)、293 个真实 PostgreSQL 用例(0 失败)、严格 mypy 与 ruff 全部通过;变更质量回执 cqr_55321547e453b9d88903 对精确指纹有效。建议按维护者流程合并。
English verdict: APPROVE - exact head 4d280ba7bb375cea1aff19955cd79f40ef14c60e; an owner-confirmed Todo can restore its exact prior text and wait under provider-revision CAS and then acquire a fresh lease, without becoming a general rollback, while schema-less historical leases are normalized once in the read boundary. The strengthened managed-Turn cases, the full control-plane TypeScript suite, and 293 real PostgreSQL authority-store tests are green; the remaining failures (module-pair vocabulary budget and six lease-renew cases) reproduce unchanged on a clean origin/main.
Merge note — #5013 merged on the reviewed exact headMerged via admin bypass as Changed surfaces: the acceptance restoration admission and its reuse of the reviewed-update CAS, the canonical lease read normalization plus its reuse in deferred reopen, the goal-frontier guidance/typing correction, an 83-line TypeScript test addition and a Python managed-Turn suite, and the bilingual ledger plus observations reference. Rebase repair: the branch's Checks on the reviewed head
Failures, skips and holds: the goal-scoped premerge gate selected 19 checks and 18 passed; Residual risk: the restoration rule must stay digest-exact so it never becomes an unowned rollback, and unknown prior values still require owner rebinding. |
Goal And Delivered Outcome
An Agent could clear an owner-confirmed Todo's existing wait while holding a hard lease, release the lease, then get stuck: stale acceptance rejected acquisition, while restoration required an active lease.
The existing TS reviewed-update boundary now permits the same claimed Agent to restore the exact prior text/wait under provider-revision CAS. The entire candidate work digest must match the existing owner binding. Criteria, ownership, lifecycle and lease lineage remain unchanged; execution still needs a fresh lease. Unknown prior values or broader changes return an actionable owner-review/rebind requirement. Closes #4971. Base:
main.Scope And Continuation
Complete for the reported text/wait restoration cycle. This is not an arbitrary history rollback or automatic acceptance rebind. Ordinary actor/exclusion admission runs first, and active leases, supplied stale execution proof, conflicting retries and bundled lifecycle/validator edits remain rejected. Managed frontier guidance names the executable route and clears its stale hold after restoration.
Negative testing also exposed a related compatibility issue: schema-less historical canonical leases could appear inactive to shared lease rules. Normalize their accepted schema once in the TS read boundary, without rewriting persisted history; deferred reopening now reuses that normalization. A small existing RPC result typing error is repaired by returning the already checked, narrowed dictionary.
The RFC ledger records native recovery progress without changing provider defaults or D1–D3. #5000 remains separately owned by Turn settlement and bounded retry; this restoration does not settle or spend a Turn.
Validation
560c1da95(377 TS/provider tests and 24 Python tests before rebase; 84 TS/provider tests after rebase; two strengthened full managed-Turn cases pass on final source).The active schema-less lease counterexample failed before normalization and now rejects restoration without changing authority. Existing lease and default-off acceptance tests remain in the provider suite. No active Goal, registry or lease was migrated or repaired in place. The managed Turn resumes its original Todo, completes, writes back and settles; repeated restoration and settlement consume quota once. No live model run is claimed. Blocked no-spend settlement/retry remains owned by #5005/#5000.
Quality receipt:
cqr_a37eb11fa376b811bb26. CI is not yet a merge-readiness claim. The main baseline has an independently observed packaged browser smoke failure, disclosed with baseline evidence in #5012.Frontend / Visual Evidence
Shared-authority RFC fixture impact
No fixture schema, provider routing or default change. Recovery admission and historical lease interpretation are qualified on real File, SQLite and PostgreSQL. General migration/D3 and the blocked no-spend retry policy remain outside this slice.
Boundary Checklist