Skip to content

fix(todos): compose public update planning and preserve retained waits - #4165

Merged
huangruiteng merged 1 commit into
mainfrom
codex/todo-update-intent-plan
Sep 10, 2026
Merged

huangruiteng merged 1 commit into
mainfrom
codex/todo-update-intent-plan

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

Summary

Compose public Todo authoring scope, external-wait topology, Monitor observation and field planning in one TypeScript call over the locked snapshot. Retire Python leaf-RPC orchestration while retaining its permission/lock/persistence responsibilities.

Fix a public writer gap: omitting resume_when from a partial edit previously bypassed external-wait validation. Clearing a retained Monitor wait's successors, changing its status to blocked, or changing its task class to Monitor could persist an invalid waiting state. These transitions now fail without writing. Explicitly clearing the condition in the same edit remains the repair path. Copy-only edits neither re-arm a satisfied wait nor reset its generation.

Issue Or Task

Maintainer-requested next cohesive stage of the TS migration and shared Goal Authority RFCs, based on merged #4143/#4149. This is a T1 public planning prerequisite, not full native update, provider promotion, or T2 atomic Monitor/successor closure. The independent lease-edit PR #4152 remains separate.

Ownership and scope

  • todos/public_update.ts composes existing typed owners, rather than duplicating their state rules.
  • The public writer replaces separate scope/wait/field RPCs with one call. update_source.py transports complete compact active/archive facts, without UI caps or Todo prose.
  • Delete external_wait_writeback.py's Python wait-route selection and baseline extraction. Keep the internal field codec for actual terminal/import callers; it must not inherit public-update restrictions.
  • Keep lifecycle/lease authority, completion effects, the writer lock, provider CAS/replay, capture and permanent Markdown projection unchanged. Locked completion proof now precedes pure field planning: stale proof failure has precedence over unrelated invalid-field diagnostics. Both cases remain no-write.
  • The diff is 15 files, +390/-184; product code is +173/-179 (net -6), tests add 170 lines, and the remainder updates the existing contracts and both bilingual RFCs. This is a cohesive rule-composition change, not a claim of full legacy-writer retirement.

Validation

  • Tested revision: fe5ef4f831170f9b6f734c3fd806c9ba8a806109, based on f75b8ee56e310f1b04be440078d8cc9c248dfd2e.
  • Run state: finished
  • Input classes: synthetic, public_fixture
Check kind Result Public-safe evidence / limitation
static passed TS typecheck, Ruff, configured mypy (21 source files), whitespace and public/private scans.
unit passed Full control-plane TS suite: 972 passed, zero failures/skips.
real_backend passed File, NoKV and isolated disposable PostgreSQL 16.15 exercised by the full TS suite. No active Goal or production database used.
integration passed Affected authoring, external-wait, field, authority, completion, Monitor and issue-fix suite: 214 passed before final transport/test refinements; final new public-update file: 7 passed.
real_entrypoint passed Actual public writer/CLI invalid-edit no-write, explicit clear repair, dry-run and one planning crossing; Todo readmodel boundary smoke passed.
integration passed Final provider-routing, unpromoted claim and split-root writer-fence regression suite: 44 passed.
regression_parity passed Three invalid partial-update cases failed before the fix. Six legal baseline/head scenarios retain identical complete persisted-state text and key response fields.
regression_parity passed Existing 464-Todo/64-lease production-scale fixture remains unchanged; all rows available to planning, without display truncation.
integration not_applicable No provider routing, promotion or projection cutover; no three-arm promotion rehearsal or elapsed-time soak claimed.

Coverage/gaps: production writer, normal and malformed edits, claim/lifecycle fencing, waiting topology, Monitor generation and retry are covered. Hosted CI has not started before publication. This does not qualify native metadata mutation, atomic successor writes, or whole-Goal promotion.

Type of Change

  • Bug fix
  • Documentation update
  • Test update

Includes a bounded refactor with deliberate behavior changes disclosed above, not a zero-behavior-change claim.

LoopX Area

  • Control plane (goals, todos, quota, scheduler, registry, runtime)

Technical Direction

  • Core control-plane hardening
  • Target base branch: main
  • Direction tracker: TypeScript control-plane migration T1; shared Goal Authority durability/promotion holds unchanged.

Shared-authority RFC fixture impact

  • Production-scale fixture schema: unchanged loopx_coordination_production_scale_fixture_v0.
  • Semantic dimensions: retained resume condition, successor topology, immutable snapshot, generation fence and partial-edit validation.
  • Provider conformance arms: File, NoKV, isolated real PostgreSQL.
  • Read-only three-arm promotion rehearsal: not applicable; no routing/projection/promotion change.

Boundary Checklist

  • No private state, credentials, raw logs, internal links or local paths in public artifacts.
  • No benchmark work duplicated.
  • Scoped to the requested migration stage and related semantic repair.
  • Every commit has DCO sign-off. Maintainer authorized command-scoped local time-gate bypass for submission only; no permanent policy change.

Future-facing pass: reuse existing typed scope/resume/field owners, delete Python composition decisions, and keep only adapters with active callers. No new capability/provider, new storage truth or automatic promotion. Review requested; no self-merge or local installation requested for this PR.

Signed-off-by: huangruiteng <huangrt01@163.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)

本次审阅绑定 base f75b8ee56e310f1b04be440078d8cc9c248dfd2e 与 exact head fe5ef4f831170f9b6f734c3fd806c9ba8a806109。结论:未发现阻塞性代码问题,代码与语义可批准;远端必需检查仍需重新跑绿后再合并。

动机

这个 PR 修的是 public Todo writer 中一个可复现的状态契约缺口。基线只在当前命令显式传入 resume_when 时调用 external-wait planner;因此一个已经带有 monitor_changed、generation fence 和独立 successor 的 Todo,如果后续只传 --status blocked、--task-class continuous_monitor 或空 successor 列表,旧 writer 会保留原 wait 却写入与它矛盾的拓扑。最小的 Python 条件判断虽能挡住三个例子,但会继续让 authoring scope、wait topology、Monitor 和 field update 分属多个决策点。把现有 typed owners 在一次纯计划中组合,才消除了同一更新快照上的规则分叉。

改动思路

权限、锁、completion proof、shadow capture、持久化和 provider fence 仍由 update_goal_todo 的既有 Python transaction 负责;它在锁内把当前 Todo、raw intent、有效 actor scope,以及需要时的完整 active/archive compact snapshot 一次性交给 todo.public_update.plan。TypeScript 依次复用 planTodoAuthoringScope、planTodoExternalWaitTransition、planTodoFieldUpdate 和 planMonitorMetadata:先算有效 scope/resume,再对实际变化的 status/task class/successor topology 验证 retained wait,最后生成字段计划。普通 text/note/evidence 修改不重新武装已满足的 wait,也不捕获新的 Monitor generation;显式 clear 仍是离开该契约的修复动作。

我也检查了保留路径:completion/supersede/import 等内部调用未带 public_context,继续走 field-only planner;这不是第二套 public policy,而是不同生命周期边界的现有调用者。新 update_source.py 只搬运旧 adapter 已使用的 resume-relevant facts,并继续使用同一个 compactor,不引入新的持久化真相或 UI 截断。

具体改动

完整 diff 为 15 个文件、+390/-184;生产代码 +173/-179(净减少 6 行),其余主要是回归测试和英中双语契约更新。

关键代码讲解

  • public_update.ts:16 的 externalWait 把“新 condition”和“retained condition + topology change”统一送入既有 typed transition;更新后的 waiting row 会先替换 role/status/task class,再检查 dependency、successor 和 generation,不再根据请求中是否出现 resume_when 决定是否验证。
  • public_update.ts:39 的 planPublicTodoUpdate 是新的组合 owner,但注释与返回契约明确它不是 admission、provider transaction 或 receipt。retained Monitor wait 重新验证时使用 transition 返回的原始 baseline,避免静默捕获更新后的 generation。
  • line_update.py:217 的 public 分支从同一锁定行集构造完整 snapshot,调用一次 effect runtime,并把 external_wait_* code 适配回原有 TodoExternalWaitAuthoringError/authoring contract;拒绝发生在 Markdown 变更之前。
  • update_source.py:8 收集 archive、user、agent 三段完整事实且不经过 display cap;Todo prose 不进入 plan。旧 external_wait_writeback.py 的 route selection 和 baseline 提取被删除。
  • todos.py:1293 删除 Python 对 scope/wait/field 三段结果的手工拼接,仍保留 authority、handoff gate、completion proof、write capture 与 settlement,因此没有扩大 TypeScript 的写入权力。

对主干的风险

最强反例不是作者已有的三个非法输入,而是 --note 同类的“省略字段是否偷偷改变旧状态”:我在 base/head 两个 detached worktree 上用同一真实 update_goal_todo fixture 跑了 10 个 case。note、evidence、text、显式相同 status/class/successor 和 clear 共七条合法路径保持相同持久化语义;尤其 copy-only 更新不返回 wait transition,generation 保持 2。三个非法 partial edit 在 base 都成功写入矛盾状态,在 head 分别以 external_wait_todo_status_must_remain_open、external_wait_todo_task_class_invalid、external_wait_successor_required 失败,且 state bytes 不变。显式相同 successor 会返回 already_waiting transition 但 changed=false,这是拓扑重验的已披露结果。

验证还包括相关 Python 七个文件 148 passed;本地完整 control-plane TypeScript 为 940 passed、1 个 PostgreSQL 环境 skip、0 failed;typecheck、Ruff、diff check 与 DCO 均通过。真实 CLI 的拒绝/no-write 和 clear dry-run 也已覆盖。PostgreSQL skip 不阻塞本 diff:这里新增的是纯 planner,实际受影响 writer 使用锁定文件 fixture,provider/promotion 没有变化。

远端当前仍为红色:一个 untouched change-window Git pull 测试偶发失败,本地单独重跑通过;Node 26 的六个 EISDIR error-text parity 失败在 base 与 head 可完全同样复现,亦不属于本 diff。pytest 和 merge-gate 只是继承前者失败。它们不是 #4165 的代码 blocker,但必需门禁应重新跑或修复后再合并,不能用本审阅代替绿色 gate。

我的整体评价

这是合理且可回滚的同一 change-reason 重构:它修复真实 public writer 缺口,同时减少 Python 决策所有权和生产行数;没有借机扩到 native update、lease、provider promotion 或原子 successor commit。typed state、domain-neutral diagnostics、machine-enforced obligation 与非权限性 plan 的边界都清楚,双语文档也披露了旧/新默认。future-facing pass 已落在最有价值的边界上,未发现需要继续扩张的 companion refactor。

English verdict: APPROVE — exact head fe5ef4f831170f9b6f734c3fd806c9ba8a806109 correctly composes the existing typed Todo owners, rejects retained Monitor-wait topology corruption without writing, and preserves copy-only --note/text/evidence semantics and the original generation fence. The current unrelated hosted failures reproduce at the base or pass on focused rerun; required checks must still be green before merge.

@huangruiteng
huangruiteng merged commit f28b13c into main Sep 10, 2026
17 of 21 checks passed
@huangruiteng
huangruiteng deleted the codex/todo-update-intent-plan branch September 10, 2026 09:24
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.

1 participant