Skip to content

feat: revise open Todo completion validators - #4833

Merged
huangruiteng merged 6 commits into
mainfrom
codex/todo-validator-revision-4832
Sep 21, 2026
Merged

huangruiteng merged 6 commits into
mainfrom
codex/todo-validator-revision-4832

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

Summary

  • add a TypeScript-owned, CAS-safe revision transaction for open Todo completion validators, with immutable digest history, actor attribution, operation replay, and private declaration readback
  • bind post-revision validation receipts to the current declaration digest so stale or unbound receipts cannot complete the Todo
  • expose the same revision state through CLI/managed Turn, Dashboard Todo details, packaged Chat assets, and bilingual operator documentation

Product entry points

  • CLI / managed Turn: todo update accepts one replacement command plus --update-operation-id, --update-expected-provider-revision, and a registered --agent-id
  • Dashboard: the existing Todo detail drawer shows validation revision, declaration digest, and revising actor; it remains read-only and does not introduce a second source of truth
  • Lark: no Lark-specific state or action is added; Lark continues to consume the canonical Todo projection

Validation

  • python -m pytest -q tests/control_plane/test_todo_completion_validation.py tests/control_plane/test_completion_validation_lane_scope.py — 56 passed
  • focused promoted end-to-end revision/readback/current-validator completion test — passed
  • CLI + coordination contract suite — 210 passed
  • TS update/completion tests — 33 passed
  • file and SQLite provider conformance, validator-revision cases — 2/2 passed for each provider (legacy and native)
  • npm run typecheck:control-plane — passed
  • Dashboard build, presentation/status schema smokes, and packaged personal-workspace browser smoke — passed
  • loopx canary premerge --from-git-diff --goal-id finance-research-goal — 19/19 passed, public-boundary scan passed

Test environment note

A full file/SQLite conformance invocation also reached two unrelated existing succession-readback failures because its spawned system Python rejects dataclass(slots=True). The new validator-revision provider cases pass under both stores, and all diff-selected premerge canaries pass.

No local Goal state, credentials, private research material, or generated dependency directories are included.

Fixes #4832

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)\n\nExact head: \n\n### 动机\n\n现有 Todo 的 completion validator 创建后不可修改;仓库移动或命令失效会让仍然有效的 open Todo 永久无法完成,并持续占用 durable work lane。这个 PR 解决 #4832:允许注册 Agent 在明确给出当前 provider revision、原 declaration digest 和 operation id 时,审计式替换 validator。\n\n### 改动思路\n\n复用现有 TypeScript Todo update authority、provider CAS 和 operation receipt,不引入第二套状态源。TS 负责 actor/lifecycle/digest admission、revision history 和 CAS;Python 只负责 canonical readback 后的 private declaration host effect,并通过同 operation id 恢复丢失响应。Dashboard、CLI/managed Turn 都消费同一 canonical projection。\n\n### 具体改动\n\n- 增加 typed completion-validation revision intent、immutable receipt/history 和 v5 update wire。\n- 增加 CLI/managed Turn replacement 参数与严格参数诊断;promoted Goal 不走 legacy fallback。\n- revision > 0 的 completion effect/receipt 强制绑定当前 declaration digest;revision 0 保持兼容。\n- Dashboard 显示 revision、digest、actor,打包 Chat 已重建;双语参考文档同步。\n- file/SQLite provider conformance、Python/TS transaction、CLI、E2E、dashboard/typecheck/build/canary 均已覆盖。\n\n### 对主干的风险\n\n最强风险是 provider commit 与 private sidecar publish 的两阶段边界:进程若在中间崩溃,该 Todo 会暂时不可完成。实现用 canonical history + same-operation replay 恢复,只补 host effect,不重复 provider mutation;stale/unbound receipt 会失败关闭。全量 provider suite 仍有既有的 spawned system Python 不支持 环境失败,但本 PR 新增的 file/SQLite conformance case 已分别直接通过,不构成本次 blocker。\n\n### 我的整体评价\n\n完成了 #4832 的完整用户路径,并符合 TS 决策归属、shared projection、provider CAS、幂等回执和产品交付规则。没有发现 blocking finding。已对 exact head 运行 result consistency check,结论为 APPROVE。由于这是作者自有 PR,GitHub 不允许 formal self-approval,因此以 COMMENTED review 记录批准结论;不自行合并,等待外部 review/merge。\n\nEnglish verdict: APPROVE - bef4ea1 implements a proportionate, typed, CAS-safe validator-revision path with current-digest completion binding, recoverable host-effect publication, shared UI readback, and no blocking findings.

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

Exact head: bef4ea1a20bc1e93929a2457888b232dc3464248

动机

现有 Todo 的 completion validator 创建后不可修改;仓库移动或命令失效会让仍然有效的 open Todo 永久无法完成,并持续占用 durable work lane。这个 PR 解决 #4832:允许注册 Agent 在明确给出当前 provider revision、原 declaration digest 和 operation id 时,审计式替换 validator。

改动思路

复用现有 TypeScript Todo update authority、provider CAS 和 operation receipt,不引入第二套状态源。TS 负责 actor/lifecycle/digest admission、revision history 和 CAS;Python 只负责 canonical readback 后的 private declaration host effect,并通过同 operation id 恢复丢失响应。Dashboard、CLI/managed Turn 都消费同一 canonical projection。

具体改动

  • 增加 typed completion-validation revision intent、immutable receipt/history 和 v5 update wire。
  • 增加 CLI/managed Turn replacement 参数与严格参数诊断;promoted Goal 不走 legacy fallback。
  • revision > 0 的 completion effect/receipt 强制绑定当前 declaration digest;revision 0 保持兼容。
  • Dashboard 显示 revision、digest、actor,打包 Chat 已重建;双语参考文档同步。
  • file/SQLite provider conformance、Python/TS transaction、CLI、E2E、dashboard/typecheck/build/canary 均已覆盖。

对主干的风险

最强风险是 provider commit 与 private sidecar publish 的两阶段边界:进程若在中间崩溃,该 Todo 会暂时不可完成。实现用 canonical history + same-operation replay 恢复,只补 host effect,不重复 provider mutation;stale/unbound receipt 会失败关闭。全量 provider suite 仍有既有的 spawned system Python 不支持 dataclass(slots=True) 环境失败,但本 PR 新增的 file/SQLite conformance case 已分别直接通过,不构成本次 blocker。

我的整体评价

完成了 #4832 的完整用户路径,并符合 TS 决策归属、shared projection、provider CAS、幂等回执和产品交付规则。没有发现 blocking finding。已对 exact head 运行 result consistency check,结论为 APPROVE。由于这是作者自有 PR,GitHub 不允许 formal self-approval,因此以 COMMENTED review 记录批准结论;不自行合并,等待外部 review/merge。

English verdict: APPROVE - bef4ea1 implements a proportionate, typed, CAS-safe validator-revision path with current-digest completion binding, recoverable host-effect publication, shared UI readback, and no blocking findings.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

动机

这个 PR 对应 #4832 的真实阻塞:一个仍然打开的 Todo 在测试路径移动后,旧的 completion validator 会稳定返回 pytest exit code 4,使 Todo completion 和后续 quota settlement 一直被挡住。当前 head 的方向是对的:给已有 validator 增加显式、CAS-safe、可审计的 revision,而不是手改 Goal 文件或把旧 receipt 重新解释成新声明的证据。

改动思路

整体链路已经比较完整:CLI 生成规范化声明;TypeScript provider 在现有 update admission、provider revision 与 operation receipt 上做原子修订;canonical Todo 只保存 digest、revision 和 history;Python 在 provider commit 后保存私有执行声明,并通过相同 operation id 支持崩溃后的重放修复;Todo completion 对 revision > 0 的 validation receipt 强制校验 declaration digest;Dashboard 只读展示 revision/digest/actor。

我沿着真实调用链检查了 todo update -> provider request v5 -> authority commit -> private declaration publication -> todo complete -> validation receipt,并检查了当前 head 相对旧审阅 head 的 provider-only projection 修复。

具体改动

  • provider 状态机要求 Todo 仍为 open/active、已有 validator、expected declaration digest 匹配,并追加连续 revision receipt;旧 digest、新 digest、actor、operation id 和时间都可审计。
  • Python adapter 在 commit 后核对 canonical readback 再写 0600 私有 sidecar;同 operation id 重试会从 history 还原原 CAS witness,因而能修复“provider 已提交、sidecar 尚未落盘”的中断。
  • completion transaction 仅对 revision > 0 要求 validation_declaration_sha256,保留 revision 0 的兼容行为,同时拒绝旧或未绑定 receipt。
  • projection 的最新修复明确把 revision/history 作为 provider-only 派生字段,Markdown 仍只携带执行当前 validator 所需的私有声明,避免制造第二个审计源。
  • 本地验证:Python focused suites 175 passed;TypeScript provider/update/completion suites 68 passed;control-plane typecheck passed;Dashboard schema smoke 和双 Vite build 通过;git diff --check 通过。

但当前 exact head 还有一个 P1 blocker:TypeScript authority boundary 没有真正类型化 revision declaration。decodeCompletionValidationRevision()declaration 只调用 canonicalAuthorityObject(),所以 { "unexpected": "accepted" } 这类完全不可执行的对象也会被哈希并提交 revision/history。我用当前 head 直接调用 planner,确认它返回了成功的 updates/receipt。随后 Python 的 _publish_completion_validation_revision() 才会尝试规范化/持久化,并在 canonical commit 之后失败,留下“canonical digest/history 已前进、私有可执行声明不存在”的 poisoned revision。

最小修复是在 TypeScript commit 之前复用/抽取 completion validation 的声明规则:只接受且规范化 validation_commandvalidation_command_argvvalidation_labelvalidation_timeout_seconds 四个字段;拒绝额外字段、双 command form、空 argv、非法 timeout 和错误类型;然后再计算 next digest。请补真实 request-v5/provider 回归,证明这些非法 payload 均失败且 provider revision、Todo digest 与 history 完全不变。

对主干的风险

正常 CLI 路径由 Python 先规范化,现有 happy-path 测试因此全部通过;风险主要来自直接 effect-runtime 调用、未来的新 adapter,或任何绕过当前 Python CLI 组装器的 provider-neutral caller。这里不能依赖“现在只有 CLI 会这样调用”,因为 PR 的目标正是把转移规则放进 TypeScript authority owner;如果 authority 接受的状态比执行端更宽,失败发生在 durable commit 之后,重试也只能重放同一个坏 revision,恢复成本比普通输入错误高。

两阶段 provider/sidecar publication 本身在声明合法时有合理的幂等恢复路径;当前 blocker 不是要求分布式事务,而是要求在不可逆的 provider commit 前完成同一份声明的语义校验。

我的整体评价

方向、边界划分和大部分恢复语义都很好,旧 receipt 不会静默满足新 declaration,最新的 provider-only projection 修复也解决了旧 head 的 parity 问题。不过“TypeScript 负责转移、Python 负责适配”的核心承诺尚未完全成立:authoritative decoder 仍允许非法 declaration state。结论是 REQUEST_CHANGES;修复上述 P1 并补 provider 不变性回归后,我愿意快速复审。

Reviewed exact head: d707c2e641245c7227bcff66ea057338641d8cb3

English verdict: REQUEST_CHANGES — validate and canonicalize the executable completion-validation declaration inside the TypeScript authority boundary before hashing or committing the revision; malformed request-v5 declarations currently advance canonical state and fail only during post-commit private publication.

Comment thread loopx/control_plane/todos/completion_validation_revision.ts Outdated
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
@huangruiteng

Copy link
Copy Markdown
Collaborator Author

已修复最新 review 指出的 TypeScript authority blocker,当前 exact head 为 2d40828cb5ad997b5ae8fdae40fe4b3e743df966

  • 抽出共享的 TS completion-validation declaration normalizer,planner 与 revision authority 使用同一语义;
  • revision commit 前严格拒绝未知字段、双 command form、空/非法 argv、非法 timeout、错误类型和无可执行 command;
  • digest 只基于规范化后的四字段 declaration 计算;
  • 新增真实 file/SQLite、legacy/native provider 回归,证明失败请求不改变 provider revision、Todo digest/history,也不产生 operation receipt。

验证:

  • PATH="$PWD/.venv/bin:$PATH" npm run test:control-plane:2217 passed, 18 skipped, 0 failed;
  • npm run typecheck:control-plane:通过;
  • focused revision/provider suites:39 passed + 8 provider conformance cases passed;
  • git diff --check:通过。

未改前端、Lark 或 CLI 入口:本次只收紧既有 request-v5 authority admission,所有入口继续走相同 canonical projection/receipt;非法 payload 会在 provider commit 前通过现有 typed failure 返回。请按新 exact head 快速复审;这是 control-plane 变更,不自行合并。

English: the TS authority now validates and canonicalizes the executable declaration before hashing or committing it, and provider conformance proves every malformed request leaves revision, digest, history, and receipts unchanged. Full control-plane tests and typecheck pass on the exact head.

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审结论

本次复审绑定 exact head 2d40828cb5ad997b5ae8fdae40fe4b3e743df966。上一轮指出的“任意/畸形 declaration 可先写入 canonical authority”已经实质修复:新增的共享 TypeScript normalizer 会拒绝额外字段、双命令形式、空 argv、越界 timeout 和错误类型,而且失败发生在 provider mutation/receipt 之前。这个方向正确,复用边界也比上一版清晰。

但当前 head 仍有 1 个 P1 原子性 blocker,因此结论仍是 REQUEST_CHANGES

核心行为与 authority 流

当前路径是:Python update_canonical_todo_if_promoted 构造 request-v5 → TypeScript decodeCompletionValidationRevision 规范化 declaration → provider 用规范化后的对象计算 digest、提交 revision/history receipt → Python _publish_completion_validation_revision 再校验 canonical digest 并写 private sidecar。

这个设计要求“TypeScript 提交的 declaration”与“Python 后置发布的 declaration”是同一个 canonical 表示;否则 canonical authority 已提交,而 private executable sidecar 无法发布。

阻塞问题

[P1] 接受的可强制转换输入会在 canonical commit 后触发 sidecar digest mismatch。

completion_validation_declaration.ts 在 strict revision 路径仍接受并转换多种非 canonical 表示,例如:

  • validation_command_argv: '["true"]'['true']
  • validation_timeout_seconds: '20'20
  • validation_label: ''null
  • 带首尾空格的 validation_command → trim 后字符串

TypeScript 使用转换后的对象提交 canonical digest;但 Python provider_update.py::_publish_completion_validation_revision 使用 request 中原始 validation_revision["declaration"] 计算 expected_digest。我实际复现了第一、二、三种组合:TypeScript 返回 ok=true 和 canonical array/int/null,而 Python 原始对象与 canonical 对象的 SHA-256 分别为 07344196...2a48d1e4...

结果不是安全拒绝:provider 会先持久化新的 completion_validation_sha256、revision 与 history receipt,随后 Python 才抛出 completion_validation_revision_publication_mismatch,private sidecar 没有对应 declaration。这样 Todo 会进入“canonical 已前进、执行声明未发布”的分裂状态,重试原始请求也不能修复 digest 差异。

最小修复有两种等价选择:

  1. strict authoritative revision 模式拒绝上述非 canonical 表示,只接受 array/int/null 等最终形态;或
  2. Python 在构造 request 前只规范化一次,并把同一个 canonical 对象同时用于 TypeScript request 与 post-commit publication。

需要补一条真实跨语言回归:从 Python adapter 进入真实 TypeScript provider,分别覆盖 JSON-string argv、numeric-string timeout、blank label、whitespace command;断言要么 mutation 前拒绝且 authority 完全不变,要么成功提交且 canonical/private digest 与 readback 完全一致。

验证证据

  • node --experimental-strip-types --test tests/control_plane_ts/todo_update.test.ts tests/control_plane_ts/completion_validation_plan.test.ts:27 passed。
  • npm run typecheck:control-plane:通过。
  • git diff --check origin/main...HEAD:通过。
  • 手工 cross-language counterexample:TypeScript 接受并规范化;Python 原始/规范化 digest 不一致,稳定复现上述 post-commit 风险。
  • 远端 PostgreSQL、desktop、dashboard 与 stage2c 检查已通过;复审时 core shards/static/forward/windows 仍在运行。CI 通过也不会覆盖这个未建模的 accepted-coercion path。

Future-facing pass

共享 normalizer 本身是合适的有界重构;下一步不要再引入第二套规则。应明确区分“transport convenience normalization”和“canonical authority declaration”,并让跨语言 commit/publication 只传递一个规范化结果。修复后这条边界会更容易局部测试、回放与恢复,无需扩展成更宽的框架。

English verdict: REQUEST_CHANGES — exact head 2d40828cb5ad997b5ae8fdae40fe4b3e743df966 fixes malformed-declaration admission, but accepted coercible inputs can still commit a canonical digest that the Python sidecar publisher cannot reproduce from the original request, causing a post-commit split state. Canonicalize once across the boundary or reject non-canonical strict-revision inputs, and add a real Python-adapter/TypeScript-provider regression.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
@huangruiteng

Copy link
Copy Markdown
Collaborator Author

已修复当前 exact-head 复审指出的跨语言原子性 blocker,新 exact head:0b765acc22142303ef3adfaad03d9ac1496a2b7e

本轮选择在 TypeScript revision authority 上拒绝非 canonical 输入,避免把 transport 便利转换带入不可逆 provider commit:revision declaration 现在必须显式包含完整四字段,并直接使用最终 string/null、string[]/null、number/null 表示;JSON-string argv、numeric-string timeout、blank label、带首尾空格 command 和缺字段都会在 provider mutation 前失败。

新增真实 Python adapter → TypeScript provider 回归,覆盖上述四个 coercible counterexample,并验证 canonical authority 与 private sidecar 都完全不变;file/SQLite legacy/native provider conformance 同步覆盖。

验证:

  • PATH="$PWD/.venv/bin:$PATH" npm run test:control-plane:2217 passed,18 skipped,0 failed;
  • npm run typecheck:control-plane:通过;
  • Python cross-language focused:5 passed;
  • file/SQLite provider conformance:4 passed;
  • git diff --check:通过。

English: strict validator revisions now accept only the final four-field canonical representation, so transport coercions cannot create a provider/sidecar digest split. Cross-language and both provider conformance regressions prove rejection happens before either canonical or private state changes. Please re-review exact head 0b765acc22142303ef3adfaad03d9ac1496a2b7e.

@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Exact-head CI 已完成:0b765acc22142303ef3adfaad03d9ac1496a2b7e 的修复相关与完整回归共 17 个 jobs 通过;唯一非绿的实质 job 是 node-minimum-compatibility,仍在旧的 10 分钟外层预算处被取消,随后 merge-gate 按预期失败。

该 CI 预算根因由 #4836 独立修复;其 exact head 1892d7f5ebea4a70e23c3e9d994f3d948eed5c7f 已在相同完整覆盖下全部通过(包括 minimum Node 和 merge-gate),当前仅等待独立 review/merge。为避免在同一 10 分钟预算下做无意义重复,本 PR 暂不盲目 rerun;#4836 合并后更新本分支并重新跑完整 required checks。

English: the repaired #4833 head completed with 17 successful jobs; the only substantive non-green lane is the known 10-minute minimum-Node cancellation, which then correctly failed the merge gate. #4836 is an exact-head all-green, coverage-preserving fix. We will refresh #4833 after #4836 lands instead of blindly rerunning the unchanged timeout.

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

Exact head: 0b765acc22142303ef3adfaad03d9ac1496a2b7e

动机

这个 PR 解决 #4832 的真实 durable-state 缺口:open Todo 的 completion validator 一旦因为测试文件移动而失效,就会稳定返回 pytest exit code 4,Todo 无法完成,绑定 Turn 的 quota settlement 也会持续被挡住;在旧契约下,安全选项只有重建 Todo 或手改状态,两者都会破坏审计或 authority 边界。

当前 exact head 给出了完整用户结果,而不只是新增一个 serializer:注册 actor 可以携带 operation id、provider revision 和旧 declaration digest,显式替换一个 open/active Todo 的 validator;旧、新 digest、revision、actor 和 operation 均可审计;以后 todo complete 只接受绑定当前 digest 的 validation receipt;CLI、managed Turn、Dashboard 和 packaged Chat 都读取同一 canonical projection。

改动思路

实现复用了现有 coordination.local_authority.todo_update、provider CAS、operation receipt 和 terminal completion transaction,没有创建第二套 Todo authority:

  1. Python CLI 只把 replacement 规范化成四字段 declaration,并构造 request-v5;
  2. TypeScript authority 在不可逆 commit 前检查 actor/lifecycle/current digest,以及 declaration 的 exact canonical shape;
  3. provider CAS 原子推进 digest、单调 revision 和 immutable history receipt;
  4. Python 读回同 operation/digest 后才写入 mode-0600 private declaration;若进程恰在两步之间退出,同 operation replay 会从 history 恢复原 CAS witness,只补 sidecar,不重复 provider mutation;
  5. completion transaction 对 revision > 0 强制校验 validation_declaration_sha256,revision 0 保持兼容。

上一轮指出的两类 post-commit split state 都已关闭。当前 decoder 不再接受任意 object,也不再把 JSON-string argv、numeric-string timeout、空 label、首尾空格 command 等 transport 便利值规范化后提交;这些输入会在 provider mutation 前失败。因此 TypeScript 提交的 bytes 与 Python 后置发布的 bytes 是同一个 canonical declaration。

具体改动

  • 新增 typed TodoCompletionValidationDeclarationCompletionValidationRevision,把四字段 shape、command/argv 互斥、timeout 范围、canonical-input 要求和 request-v5 downgrade 防护集中到 authority owner。
  • planCompletionValidationRevision 只允许 open、active、已有 validator、旧 digest 匹配且 actor 已注册的 Todo;相同 digest、stale CAS 和 terminal state 都 fail closed。
  • provider record 只保存 public-safe digest/revision/history;private executable command 仍留在 runtime-root store,Dashboard 仅展示 revision、digest 和最新 revising actor。
  • completion transaction 对修订后的 validator 绑定当前 digest,旧 receipt 不会因为 decision code 相同而满足新 declaration。
  • file/SQLite legacy/native provider、real Python adapter → TypeScript runtime、CLI diagnostics、completion transaction、Dashboard schema/build 和 packaged Chat 都有对应覆盖。

关键代码讲解

  • completion_validation_declaration.ts::normalizeTodoCompletionValidationDeclaration 是 canonicality choke point。revision route 同时启用 strict_fieldsrequire_commandrequire_canonical_input;它现在要求四字段齐全并已是最终 string/null、string[]/null、integer/null 表示,避免“TS 先转换并提交、Python 再对原值哈希”的分裂。
  • completion_validation_revision.ts::planCompletionValidationRevision 在现有 Todo/provider head 上验证 lifecycle、expected digest、actor 和 history,再产生 next digest 与不可变 receipt;这让 replay、审计和 stale-writer 拒绝归属于一个 state-machine owner。
  • provider_update.py::_publish_completion_validation_revision 只在 canonical readback 中找到同 operation 且 digest 相同的 history receipt 后发布 private declaration并立即读回;它是 effect bridge,不重新决定转移。
  • completion_transaction.ts 把 revision > 0 的 effect/receipt 与 current declaration digest 绑定,保留 revision 0 兼容,因此历史 validation receipt 不会被静默重解释。

对主干的风险

最强残余风险仍是 provider commit 与 private declaration publication 的两阶段边界:合法 revision 已提交后若进程崩溃,该 Todo 会暂时无法执行新的 validator。这里不需要伪装成分布式事务;当前 history + same-operation replay 已提供确定恢复路径,且 readback mismatch 会显式失败,不会把缺失 sidecar 当成成功。

我重点复核了此前容易被 happy-path 测试漏掉的 negative paths:任意字段、缺字段、双 command form、空 argv、越界 timeout、JSON-string argv、numeric-string timeout、空 label 和 padded command 都在 commit/receipt 前拒绝;跨语言回归同时断言 canonical authority 与 private sidecar 完全不变。

本地复审证据:

  • uv run --extra test python -m pytest -q tests/control_plane/test_local_coordination_authority.py -k validator_revision:5 passed;
  • node --experimental-strip-types --test tests/control_plane_ts/todo_update.test.ts:27 passed;
  • npm run typecheck:control-plane:通过;
  • git diff --check origin/main...HEAD:通过。

远端 exact-head 有 29 个成功检查;node-minimum-compatibility 仍因已知的主干 10 分钟预算被取消,merge-gate 因此失败,且当前 branch 为 BEHIND。对应预算修复在 #4836 独立处理。因此本 review 认可代码语义,但不表示当前 head 已 merge-ready:需要基线修复合入后更新分支并重新取得完整 required-check 绿灯。

我的整体评价

结论为 APPROVE。这个 PR 的确比一次普通字段更新更宽,但问题会永久阻塞 durable workflow,手改状态又不可接受;在 public digest/private command 分离、provider CAS、幂等 replay、旧 receipt fencing 和三类产品入口的约束下,当前 scope 是相称的。最后一轮还把 declaration semantics 收敛到一个 TypeScript normalizer,属于与本改动同原因、可回归验证的有界 future-facing refactor;没有必要再引入更宽的框架。

未发现新的 blocking finding。由于这是作者自有 PR,GitHub 不允许 formal self-approval,所以用 COMMENTED review 记录批准结论;合并仍须经过 repository policy,并在同一 exact head 上重新通过 merge-readiness gate。

English verdict: APPROVE — exact head 0b765acc22142303ef3adfaad03d9ac1496a2b7e now fences both malformed and coercible non-canonical revision inputs before provider mutation, preserves one TypeScript transition authority, supports deterministic sidecar recovery, and binds completion to the current declaration digest. The code review has no blocking finding, but the PR is not merge-ready while it remains behind and the known minimum-Node timeout keeps the required merge gate red.

…revision-4832

Conflicts were limited to the packaged chat bundle
(loopx/web/chat/index.html, asset-retention.json and the hashed entry
assets), which both sides had regenerated. Resolved by rebuilding the
bundle from the merged dashboard source (npm run build:chat) so the
packaged entry includes both the validator-revision readback and main's
personal-workspace work, with the bounded two-generation retention
manifest intact.

Signed-off-by: LoopX Agent <agent@loopx.local>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
@huangruiteng
huangruiteng merged commit f19ecaa into main Sep 21, 2026
8 checks passed
@huangruiteng
huangruiteng deleted the codex/todo-validator-revision-4832 branch September 21, 2026 09:09

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

Exact head: fdc98c1ebefb72acc7bb52482dbb4042e23200fd(PR head 0b765acc2main@855c8ca0d 的合并;随后作为 f19ecaa1d 合入 main,该 merge 的 tree 与本 head 逐字节一致git diff fdc98c1eb origin/main 为空)

动机

#4832 的核心缺陷有两半:已晋升 Goal 的开放 Todo 无法更换完成验证器(只能重建 Todo,代价是丢掉 Todo 身份、历史、lease 与链接);而完成回执不携带声明身份,所以一条为旧命令签发的回执(或未绑定摘要的历史回执)可以满足一个已经被替换的声明——等于用错误的命令证明完成。本 PR 用一把 TypeScript 拥有的、CAS 安全的修订事务同时解决这两半,并把审计事实落在 canonical provider 上。

改动思路

沿用仓库既有的"TS 决策、Python 适配"边界,不新增第二套权威:

  • 决策在 TScompletion_validation_revision.ts 负责"这次替换是否成立"——Todo 必须 open 且 active、必须已有声明、CAS 摘要必须匹配当前值、不允许无变化替换、必须有已注册 actor、历史必须规范且连续;成功则产出新摘要 + 单调 revision + append-only 回执。
  • 声明语义只留一份:把原先埋在 planner 里的字段解析抽成 completion_validation_declaration.ts,planner 与 revision 路径共用;否则这次改动会变成第二份实现。这是本 PR 最有价值的一处去重。
  • 提交只有一次 provider CAStodo_update.ts 新增请求 v5,修订意图与普通编辑互斥,并把修订回执同时放进 dry-run plan 与 commit receipt。
  • Python 只做适配与私有存储provider_update.py 负责构造 CAS 见证(丢响应重试时从持久历史里还原原始 previous_declaration_sha256,让同一 operation id 哈希一致,而不是被误判成冲突写入),并在 provider 成功之后才落私有命令声明,且必须读回 canonical 历史里带有匹配回执才允许发布。
  • 完成端绑定completion_transaction.ts 在 revision > 0 时要求 effect 与回执都携带当前声明摘要,Python 侧把该摘要写入回执并校验形状。

具体改动

关键代码讲解

  1. loopx/control_plane/todos/completion_validation_revision.ts::planCompletionValidationRevision —— 新权威。依次校验 open/active、声明存在、previousDigest === expected_declaration_sha256(CAS)、nextDigest !== previousDigest、actor 已注册、历史长度与末位摘要自洽,最后返回 {updates, receipt}。纯函数,无 I/O,唯一生产调用方是 executeCoordinationTodoUpdate
  2. loopx/control_plane/todos/completion_validation_declaration.ts::normalizeTodoCompletionValidationDeclaration —— 共用声明规范化,带 strict_fields / require_command / require_canonical_input 三档严格度:规划路径保持历史宽松,修订路径强制 canonical(拒绝 JSON 字符串形式的 argv、数字字符串 timeout、空白 label、缺字段),把"transport 便利转换"挡在不可逆 provider commit 之前。
  3. loopx/control_plane/coordination/todo_update.ts(请求 v5 分支)—— normalizeTodoUpdateInput 拒绝"修订 + 任何其他编辑"的组合;修订结果写进 plan 与 commit 两份回执,clear_fields 不再能清掉本次计划字段,提交前用 canonicalTodoRecord(next) 复验记录。
  4. loopx/control_plane/todos/completion_transaction.ts::reduceTodoCompletionTransaction —— requiresDigest = completion_validation_revision > 0;为真时计算四字段声明摘要并要求回执摘要完全一致,否则 EffectRuntimeRequestError,不回退成"接受未绑定回执"。
  5. loopx/control_plane/todos/provider_update.py::_completion_validation_revision_request / _publish_completion_validation_revision —— Python 适配:前者构造 CAS 见证并支持幂等重放还原,后者在 canonical 读回与声明摘要、operation id 不一致时抛 completion_validation_revision_publication_mismatch,而不是写入一个 canonical head 不承认的私有声明。

其余:coordination_state_contract_v0.json 增加两个 Todo 字段(生成件同步);completion_validation_projection.py 为老记录补 revision=0 / 空历史;completion_validation.pycompletion_transaction.py 接受并传递 validation_declaration_sha256;CLI 侧新增四个 validation 参数与"恰好一种命令形式 + operation id/provider revision/agent id + 不可与其他编辑合并"的前置校验;Dashboard todoItemSchema 增加 revision/history,context-drawer 只读展示 revision/digest/修订者(含中英文案);machine_section_projection.py 明确这两个审计字段不参与 Markdown parse/render parity;文档写进既有的 docs/reference/canonical-todo-completion-update.md(中英双语,而非新开文档)。

合并冲突的处理

冲突只有三处,全部是打包产物loopx/web/chat/index.htmlasset-retention.json 以及 hash 化的入口资源(双方各自重打过 bundle)。我没有挑一侧,而是在合并后的源码上重新执行 npm run build:chat 得到新入口 index-B_m3L4fM.js:产物同时包含本 PR 的 验证声明摘要 / Validation declaration digest / Revised bymain产物版本 / 证据内容 / 阅读报告asset-retention.json 保持"新一版 + 线上正在服务的一版"两代上限,旧共享代与 PR 侧临时产物由保留策略回收。

两条路径

  • 准入路径:CLI/Turn → 单意图校验 → CAS 见证(含丢响应还原)→ TS 规划(CAS + 历史追加)→ 一次 provider commit → 读回校验后落私有声明 → 后续完成时回执必须携带当前摘要。
  • 拒绝路径:陈旧 CAS / 可强转的非 canonical 输入 / 与其他编辑合并 / 缺 actor / 终态或归档 Todo / 无变化替换 → 全部在 provider 变更之前给出 typed 失败,且不改变 provider revision、摘要与历史。

对主干的风险

  • 可回滚、失败关闭:请求 v5 是新增 schema,旧二进制会拒绝而不是部分应用;撤销或降级前需先完成/重试未决的投影投递。没有删除任何持久记录或历史回执。
  • 已验证(exact head)npm run test:control-plane → tests 2298 / pass 2278 / fail 0 / skipped 20(242s);npm run typecheck:control-plane 通过;5 个目标 Python 套件 241 passed(含新增 183 行 authority 用例与 65 条 CLI 诊断);provider parity/file/sqlite/journal 42 passednpm run build:chat 重打并核验产物内容,pwa-bundlepresentation-surface-schema smoke 通过;loopx canary premerge --from-git-diff --git-diff-base origin/mainstatus=passedmerge_gate_passed=true、0 failures、0 manual holds
  • 未验证:本 head 的远端 required checks 在 PR 被合并时仍在跑(合并未等待 CI)——这是最强的证据缺口;本轮回放走的是真实 file/SQLite canonical provider,但没有 PostgreSQL 实例、也没有真实 host 模型 turn 完成一次修订;Dashboard 只做了构建/schema/读回校验,未做浏览器验收。
  • 两条非阻塞 P3(详见评审结论,不构成阻塞):①"恰好一种命令形式 / 不可与其他编辑合并"这两条规则目前同时写在 CLI 参数校验、库入口和 TS intent normalizer 三处(TS 是权威,Python 两份是前置友好报错),建议下次触碰时收敛为一份;②摘要绑定按 revision > 0 生效,从未修订过的 Todo 仍接受未绑定回执——这是为在途回执保留的兼容例外,建议在文档中显式写清并用一条断言钉住边界。

我的整体评价

这是一个边界清楚、去重正向的 control-plane 能力切片:权威只有一处(TS 修订事务),声明语义因为这次改动从"planner 私有"变成"共用一份",完成端用摘要绑定把"回执证明了哪份声明"变成可判定事实,审计历史 append-only 且可重放;Python 侧只保留适配与私有存储,并在读回不符时失败而不是继续发布。它把一个"只能重建 Todo"的运维动作变成受 CAS 与审计保护的一等操作,同时没有引入第二编辑权威、默认 provider 变更或 Lark 专用状态。

结论:APPROVE(本 head 无阻塞问题)。建议后续把上述两条 P3 在相关模块下一次改动时一并处理,并确认该 head 的远端 required checks 收口。


English verdict: APPROVE - Audited merged exact head fdc98c1 (PR head 0b765ac merged with main@855c8ca0d; the resulting tree is byte-identical to the merged main commit, git diff fdc98c1eb origin/main is empty). The PR adds one TypeScript-owned CAS-safe revision authority for open Todo completion validators (planCompletionValidationRevision), shares one declaration semantics between the planner and the revision path (completion_validation_declaration.ts), binds completion receipts to the current declaration digest (completion_transaction.ts, revision>0), publishes the private command declaration only after verified canonical readback (provider_update.py), and surfaces readback-only revision/digest/actor in the Dashboard with bilingual docs; it introduces request schema v5 plus two additive Todo audit fields and no second editor, provider default or Lark state. Merge conflicts were only regenerated chat-bundle artifacts and were resolved by rebuilding from merged source (new entry index-B_m3L4fM.js containing both the PR's and main's dashboard work, retention held at two generations). Validation at this head: TS control-plane 2278 pass / 0 fail / 20 skipped, typecheck clean, 241 Python target tests passed, 42 provider/authority conformance tests passed, packaged chat rebuild plus pwa-bundle and presentation-surface-schema smokes ok, and the exact-diff canary premerge gate passed with 0 failures and 0 manual holds. Two non-blocking P3 notes (intent rules expressed in three places; digest binding starts only after the first revision) plus residual risk: remote CI for this head was still running when the PR was merged, no PostgreSQL provider or live host turn was exercised, and the Dashboard drawer was verified by build/schema/readback rather than browser acceptance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Control plane: existing Todo completion validator cannot be updated after test path moves

1 participant