Canonical User Todo completion updates
September 20, 2026 · View on GitHub
A promoted local Goal routes todo update --status done for User Todos
through the TypeScript terminal transaction. File and SQLite use the same
semantic owner; PostgreSQL exercises it through the service-owned provider.
This does not select a default provider or promote an existing Goal.
loopx todo update --goal-id example --todo-id todo_observation \
--agent-id agent-a --status done --note 'Observed outcome verified' \
--no-follow-up --update-operation-id observation-completion
Use the same operation id and intent after a lost response. A new annotation
uses a new id (omitting the id generates one). --dry-run validates and previews
without running declared validation, writing a receipt, or delivering a display.
Agent completion continues to require loopx todo complete.
Revising an open Todo validator
A promoted Goal may replace an open, active Todo's declared completion validator without recreating the Todo. Read the current provider revision, then send the replacement as a dedicated reviewed edit:
loopx todo update --goal-id example --todo-id todo_observation \
--agent-id agent-a \
--validation-command-json '["python3","-m","pytest","-q","tests/new_test.py"]' \
--validation-label 'focused validation' \
--update-operation-id revise-validator-1 \
--update-expected-provider-revision file:42
The TypeScript transaction compares the current declaration digest, commits the new digest, monotonic revision and public-safe audit receipt under one provider CAS, and rejects terminal, archived or stale edits. The Python boundary stores the private command declaration only after provider success and verifies its readback. Reuse the same operation id, expected revision and replacement after a lost response; a different intent requires a new operation id and a fresh read. Validator replacement cannot be combined with another Todo edit.
Completion receipts for a revised validator bind the current declaration digest. A receipt issued for the previous command, or an unbound legacy receipt, cannot satisfy the replacement. CLI and managed Turn use the same facade. The Dashboard Todo details show the current revision, digest and last actor; it is readback only, so no second editor or Lark-specific authority is introduced.
修改开放 Todo 的验证器
已晋升 Goal 可以在不重建 Todo 的前提下替换开放且仍 active 的完成验证器。调用方先 读取当前 provider revision,再把新命令作为独立的 reviewed edit 提交。TypeScript 事务在同一次 provider CAS 中核对旧声明摘要,并提交新摘要、单调递增的 revision 和 公开安全的审计回执;已完成、已归档或基于旧 revision 的修改会被拒绝。Python 边界 只在 provider 成功后保存私有命令声明,并校验读回结果。丢失响应时复用相同的 operation id、expected revision 和替换内容;新的意图必须使用新的 operation id 并 重新读取。验证器修改不能和其他 Todo 编辑合并提交。
修改后的完成回执必须绑定当前声明摘要,因此旧命令产生的回执或未绑定摘要的历史 回执都不能完成新验证器。CLI 与 managed Turn 复用同一 facade;Dashboard 的 Todo 详情只读展示 revision、digest 与最后修改者,不新增第二套编辑权威或 Lark 专用状态。
One edit, one terminal transaction
The update decoder, authoring planner and record materializer are shared with ordinary edits. The terminal owner checks the original Todo's completion authority and the edited record's update authority; clearing a claim or binding cannot turn an update-only grant into a completion grant. Existing lease ownership/requirement restrictions remain: an annotation cannot rewrite an execution grant. Supplied lease proof must identify a current execution; expired explicit proof does not enable automatic reacquisition.
The transaction checks linked successors before issuing validation effects. Self-links and missing successors fail without running a caller command. This ordering also applies to the existing complete/supersede transaction. Completion does not invent a decision outcome: use the explicit decision workflow when an approval, rejection or cancellation must be recorded.
A declared validation command runs in the host after TS admission. The resumed update binds the issued provider revision, retains the registry source witness, and refreshes the clock. Any intervening provider commit rejects that resume, even an unrelated Todo edit. Retry re-reads and revalidates; validation itself may have external effects. Registry witnessing is optimistic, not an atomic cross-resource authorization transaction or an executor-held effect fence.
Todo fields, completion state, lease release, projection intent and the business receipt commit together. Release preserves the existing lease version and epoch. Markdown is a display projection and is not required to admit a completion. Private validation declarations remain in their private store; they are neither imported from an untrusted display nor embedded in public completion receipts.
Linked User completion effects
An admitted todo complete --decision-outcome approve|reject|cancel now commits
its exact linked Agent Todo effects in the same provider transaction as the
User completion and receipt. Ordinary User actions completed through update or
Chat use the same rule. todos/user_completion.ts owns this decision for both
canonical providers and the legacy Markdown adapter; Python retains locked
snapshot extraction and writeback, not a second scope/resume implementation.
| Decision / current target | Effect |
|---|---|
| Approve a linked gate | Consume only covered required scopes and their recorded negative outcomes; preserve independent requirements. |
| Reject or cancel a linked gate | Keep requirements, replace the latest outcome for that exact scope, preserve independent outcomes, and block the active target. |
| Complete a linked User action | Attempt resume without consuming decision authority. |
| Another active linked User Todo, remaining requirement or negative outcome | Keep the blocked target blocked. |
| Explicit blocker task | Require explicit blocker repair. |
| Completed, deferred or archived target | Do not change or reactivate it. |
Approval can consume a requirement on an already open target. Resume preserves its claim; it does not acquire or transfer the Agent target's lease. The existing exact-gate auto-acquire/release contract still applies to the completing User gate itself. Supersede never runs approval effects. An unrelated Todo or unlinked standing approval is outside this exact-target mutation.
This fixes canonical completion previously leaving an approved target stranded. It also intentionally tightens both legacy and canonical behavior: partial approval cannot resume work with unmet requirements, and late rejection cannot revive terminal/deferred work. Normal completion without a linked target is unchanged. No provider selector or capability default changes.
unblock_resume and decision_scope_resolution are historical business results,
not fresh permission. Existing receipt identities remain valid. Replaying a
pre-fix receipt does not retrofit missing effects; an already completed gate is
not a new owner decision. Reconcile such an inconsistent target against the
recorded decision through an explicit reviewed repair. Never reopen a gate just
to obtain another approval, or change an operation id to reinterpret old intent.
Concurrency rejection writes neither the User completion nor its dependent effects. After a lost response, recover the same receipt; after display loss, rebuild from the current canonical head. The packaged Chat HTTP tests cover linked User-action completion, failed validation, readback and proposal retry. The existing decision-gate UI directs explicit decisions to the CLI, so no new frontend control or Lark permission surface is introduced.
关联 User 完成与目标 Agent Todo 的决策消解、阻塞状态、原操作回执在同一事务内提交。 旧 Markdown 路径也调用同一个 TS 规则;Python 仅保留锁内快照适配与写回。批准只消解 覆盖的要求,拒绝/取消保留要求并记录结果;普通 User action 不消费授权。其他关联 User Todo、剩余要求或拒绝结果仍会阻止恢复;已完成、延期、归档任务不会被晚到决定 重新激活。上述两项安全修复同时影响旧路径和 canonical 路径,其他默认值不变。 重放返回历史回执,不补做旧版本遗漏的联动,也不产生新的授权;历史不一致须根据 原决定显式核对修复。目标任务的执行租约与用户批准仍是不同合同。
Recovery and callers
- Historical replay precedes current source admission and returns the original business receipt. Changed edit intent with the same operation id is rejected.
- An already completed User Todo can receive a new completion-update annotation without rerunning validation or changing its completion timestamp. This does not reopen the Todo or grant further execution authority.
- The existing Chat User-completion action uses its reviewed canonical revision and proposal operation id. Failed validation produces a failed proposal with no success receipt. Pending display delivery is retryable; a retry recovers the canonical operation before checking present-day freshness, then projects the current head. Agent completion keeps its existing dedicated route.
- CLI and Python use the same public facade. No frontend layout, action name, status selector or provider setting is added. The shared typed review plan now exposes the original-operation retry for canonical User completion too. The existing completion button and failed-proposal retry interaction remain the user entry points.
The same synthetic pending-completion proposal on the packaged desktop surface: before: omitted from recoverable proposals, after: the original-operation retry. These images use the repository's synthetic workspace fixture, not a live Goal. No responsive layout changes are involved.
The local update transport uses request v3 for the completion envelope. v0–v2 reject that envelope instead of silently accepting only part of the intent. Their ordinary update behavior and receipt encoding remain unchanged. The provider-neutral terminal receipt includes the User edit identity only for this new operation family; existing complete/supersede receipt identities remain valid.
Migration boundary
Unpromoted Goals retain their existing Python Markdown/event adapters. The shared host validation executor and failure projection replace duplicated transport plumbing; the TS edit decoder/materializer is no longer owned only by the ordinary update transaction. Permanent rendering and private command execution still have real Python callers and are not retirement candidates.
This closes the User completion-update caller within TS T1/T2 and local-default L2. It does not close every Monitor/event caller, executor-held effect fencing, D1 consumer recovery, SQLite D2 capacity/elapsed soak, or D3 whole-Goal cutover. See the local-default program.
Rollback the code before using the new caller, or finish/retry its outstanding projection delivery before downgrading. Older binaries reject request v3 and cannot recover this operation through the old update route. Existing durable Todo/lease records, historical receipts and permanent import/export obligations are not removed by this change; never revive stale Markdown as authority.