Skip to content

fix(issue-fix): close grouped Monitor execution and recovery - #4862

Merged
huangruiteng merged 3 commits into
mainfrom
codex/authority-execution-closure
Sep 22, 2026
Merged

huangruiteng merged 3 commits into
mainfrom
codex/authority-execution-closure

Conversation

@huangruiteng

@huangruiteng huangruiteng commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

Goal And Delivered Outcome

Close the retained issue-fix grouped Monitor execution caller under the shared-authority and TS migration roadmap (#4574, #3225, #3245).

Before, the public PR-lifecycle command could create a grouped Monitor but could not observe changed membership or stop an empty bucket under hard leases. The immutable main baseline fails the member-change step on File/SQLite and the empty-group step on legacy. The caller now completes create → observe → stop → reactivate through the existing typed Todo and lease owners, with recovery after interrupted execution.

Base: main. This closes one real L4 caller; it does not change provider defaults or complete whole-Goal migration.

Behavior And Refactor

  • Move complete bucket selection and reconciliation decisions into one TS capability planner. Remove the Python grouping/fingerprint/transition implementations; runtime Python shrinks by 76 lines. Reuse scheduler cadence, scope decoding, Unicode ordering, Todo transactions and projection delivery.
  • Acquire a bounded execution for hard-lease observations/stops, revalidate the plan, and release only that attempt. The attempt is identified by the reconciliation subject it executes against (Goal, Monitor Todo, watched bucket, actor), not by the observation clock or the raw ledger rows, so a fresh invocation that retries interrupted work recovers its own active acquisition while a key minted for another subject or actor never matches. Same Agent identity still does not authorize borrowing a different execution. After an observation commit, retry cleans its surviving lease without repeating the Todo write. Expired/released attempts acquire anew. The post-acquire typed re-plan still validates the decision: a changed operation, membership digest, material-change verdict or write scope stops the stale step, while the observation clock and the schedule it derives are re-derived for the execution that actually writes.
  • Explicit lease-backed legacy Monitor observation uses the existing native held fence under the Markdown writer lock. Ordinary legacy update admission remains unchanged.
  • Reject missing/malformed ledger evidence, ambiguous active targets and older empty observations before they can be interpreted as completed work. Archived/superseded history cannot shadow a current Monitor. Preserve existing Unicode membership digests.
  • Pass the existing --runtime-root override through every affected read/write and use the explicit priority parameter for creation. No new capability, provider, settings surface or raw-patch authority.

Buckets remain independent commits. A later failure does not undo earlier committed buckets; read back and retry the observation. Unchanged retry can perform lease cleanup and display delivery while reporting no Todo business write. This is not an external-tool fencing protocol or an all-bucket atomic transaction.

Validation

  • Retry identity repair (this head): the reviewer's fresh-invocation counterexample now passes on all three providers - first run exits after the durable acquire, second run observes one second later and keeps the same lease key/epoch, then releases it. The same retry also succeeds when the ledger is re-ordered but semantically identical, and an unrelated execution with the same actor is still refused.
  • Tested revision: cd5bf1b19d9b5c3a6c9e47ffcbcdf9ee88184509.
  • Run state: finished.
  • Input classes: synthetic, public_fixture, authorized_private_read_only.
Check kind Result Public-safe evidence / limitation
unit / integration passed Final head: 94 Python tests covering the public CLI, explicit runtime-root routing, soft/hard modes, foreign execution rejection, retry, Monitor admission/configuration and lease compatibility. Includes real child-process exit after durable observation.
real_backend passed Final head: 24 TS tests, zero failures/skips. Native/imported complex fixtures run observation, stop and reactivation against File, SQLite and isolated PostgreSQL 16; non-target Todo/lease records remain unchanged.
regression_parity / real_entrypoint passed Read-only local-source capture with 1,012 records and 10 leases; all six CLI stages pass on three disposable legacy/File/SQLite runtimes, with frozen source and all original records unchanged. Immutable original main reproduces the failures above. This rehearsal preceded integration of unrelated ready-work changes; final affected tests were rerun afterward.
integration passed Broad TS run before that main integration: 2,525 passed, zero failures. Its one optional PostgreSQL service skip subsequently passed separately on another isolated database. An earlier standalone store command selected an incompatible system Python for four subprocess checks; the corrected full run uses the selected checkout environment and passes.
real_entrypoint passed Built wheel installed in an isolated environment; its packaged native reconciliation handler executes outside the checkout.
premerge passed Final-head risk-based canary: all 19 selected checks passed, zero failures; no manual holds.
static passed TS typecheck, configured mypy (22 sources), focused Ruff, diff hygiene, and all 16 paths classified/scanned for private material.

Cold CLI observations on the retained source took roughly 3–18 seconds per invocation under concurrent validation load. Acquiring and revalidating has a cost; this PR claims correctness and ownership convergence, not throughput improvement. Further batching belongs to the existing transaction-payoff workstream.

Exact-scope quality receipt: cqr_232a33f42dcbc0e8bfc4, verified valid; zero blockers/warnings, one disclosed efficiency advisory, no policy safe-fix pass.

Scope And Continuation

Complete for the grouped Monitor caller. Bilingual operator docs and RFC checkpoints describe the changed error/recovery behavior and retain the conditional 5–8 remaining delivery-package estimate: remaining execution callers/fences, D1 consumers, contributor-owned SQLite D2 (#4224), capture/whole-Goal qualification, and default onboarding/retirement. No active Goal was promoted or used as a mutation fixture. PostgreSQL deployment, capacity and elapsed soak remain separate.

The affected entrypoint is the existing issue-fix CLI and its shared command template. No frontend change is needed: no setting, action schema or visual surface is introduced; existing Todo rendering and projection recovery are reused. No first-screen changes. The bounded future-facing pass was applied at the planner/execution boundary; no speculative provider framework was added.

Two DCO-signed commits separate runtime behavior from durable validation and documentation. Maintainer review and merge are required for this control-plane change.

@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.

Request changes conclusion (author-owned PR; GitHub blocks formal self-review)

Exact head reviewed: cd5bf1b19d9b5c3a6c9e47ffcbcdf9ee88184509

动机

这个 PR 的目标是对的:把 grouped issue-fix Monitor 的 eligibility/grouping/operation decision 收到 TypeScript typed planner,Python 只负责读取完整 ledger、执行 public Todo writers、持有并释放 lease。它解决的也不是表面问题——空 bucket 要真实完成,membership 变化要被观察,进程中断后同一执行还要能恢复,不能借用别人的 lease。

我没有只验证正常 observe/complete,而是把“中断后恢复”当作最强产品承诺来反证。结果发现当前 exact head 只在测试复用完全相同 generated_at 时可恢复;真实 CLI 重跑会生成新的时间戳,因此会把自己的旧 lease 当成另一次执行并被冲突拒绝。

改动思路

总体边界合理:

  • planIssueFixMonitorReconciliation 从完整 ledger 与 canonical Todo 状态生成 typed steps;
  • Python materializer 在 effect 前 acquire,拿到自己的 execution proof 后 re-plan,再调用 add_goal_todo / update_goal_todo / complete_goal_todo
  • _release_attempt 只允许相同 owner、相同 attempt prefix、相同 proof 释放,避免误放别人的执行;
  • legacy/file/SQLite 都走同一 lease/public-writer contract。

阻塞点不在这个分层,而在 attempt identity 的输入:

identity = json.dumps(
    [goal_id, step.get("todo_id"), claimed_by, generated_at, cadence, rows],
    sort_keys=True,
    separators=(",", ":"),
)
prefix = "issue-fix-monitor:" + sha256(identity) + ":"

generated_at 是每次命令的 observation time,rows 还是原始有序列表;它们不是同一业务执行的稳定身份。进程在 durable acquire 后退出时,下一次正常命令即使 Goal、Todo、actor、cadence、ledger 语义和 planned operation 全部相同,只要时间变化一秒,prefix 就不同。随后代码拒绝复用旧 key、生成新 key,再被现存 active lease 拒绝。

具体改动

本 PR 已经完成且验证有效的部分:

  1. TypeScript planner 正确接管 group construction、add/observe/reactivate/complete/unchanged 的决策;
  2. Python effect adapter 使用现有 public Todo/lease API,没有再保留第二套业务 transition owner;
  3. effect 前 acquire、acquire 后 re-plan、effect 后 exact-proof release 的顺序合理;
  4. complete ledger、explicit boolean、namespace、priority 和 projection 字段都采用 typed contract;
  5. 三个 provider 的正常 execution、lost response、wrong actor/lease 与 projection settlement 覆盖扎实。

但需要修复以下阻塞问题:

[P1] 真实重试的 generated_at 变化会破坏自己的 lease recovery

  • 位置:loopx/capabilities/issue_fix/pr_monitor_materialization.py:99-136
  • 触发:第一次执行在 acquire_task_lease 已持久化之后进程退出;相同命令重新运行,generated_at01:00:00Z 变为 01:00:01Z
  • 实际结果:legacy 报 todo already has an active lease,file/SQLite 报 todo_lease_conflict
  • 影响:Monitor bucket 被卡到 TTL 到期或人工释放,和 PR 的“interrupted execution recovery”目标直接相反;
  • 为什么现有测试没发现:test_retry_after_interrupted_acquisition_recovers_its_own_attempt 和 real-process retry 都把两次 generated_at 固定成完全相同的值,因此恰好得到同一 prefix。

最小修复建议:

  1. attempt prefix 由稳定的执行意图派生,例如 Goal/Todo/actor/typed target operation 与必要的稳定 source fingerprint;不要包含每次 observation 的时间戳或 incidental list order;
  2. source freshness 继续在 acquire 后的 typed re-plan 中单独校验,不能为了稳定 key 而复用陈旧业务决定;
  3. 保留“unrelated execution 即使同 actor 也不能借 lease”的现有负例;
  4. 增加三后端回归:第一次在 acquire 后退出,第二次使用更晚 generated_at,要求成功恢复并保持同一 lease key/epoch;最好再覆盖语义相同但 ledger 行顺序变化。

对主干的风险

exact head 的正常路径质量较高:

  • focused Python suite:37 passed(70.37s);
  • source-checkout TypeScript/authority suite:419 passed(189.71s);
  • ruff check:通过;
  • git diff --check:通过;
  • 对当前 origin/maingit merge-tree --write-tree:无冲突。

第一次直接调用 system node 时,子进程误用了系统 Python,出现 dataclass(slots=...) 环境错误;随后改用 uv run --extra test node ...,完整套件全绿。这一环境纠正不影响上面的 blocker。

我另外运行了独立的真实边界负例,只改变重试时间戳,legacy/file/SQLite 3/3 全部失败。临时测试文件已删除,两个 review worktree 都保持干净。这个失败比正常 suite 更接近实际 CLI 行为,因此不能用现有绿测覆盖掉。

我的整体评价

结论是 REQUEST_CHANGES。TypeScript 决策权、Python effect boundary、public writer/lease 复用以及 normal-path tests 都是正确方向;我不建议推翻这套设计。需要阻止合并的是一个很窄但直接命中核心承诺的语义错误:把 observation timestamp/raw row ordering 当成 recoverable execution identity。

修复后请不要只重跑现有同时间戳测试;要让 fresh-invocation counterexample 在三个 provider 上先红后绿,并重新检查 unrelated lease 仍然拒绝。完成这个最小修复后,PR 的整体结构有机会直接通过复审。

English verdict: REQUEST_CHANGES - exact head cd5bf1b19d9b5c3a6c9e47ffcbcdf9ee88184509 has a blocking recovery bug: the lease-attempt prefix includes generated_at, so a normal retry after a post-acquire crash cannot recognize its own active lease; a one-second timestamp change reproduced todo_lease_conflict on legacy, file, and SQLite despite the existing same-timestamp suites passing.

@huangruiteng
huangruiteng force-pushed the codex/authority-execution-closure branch 2 times, most recently from d062f2c to e6d9ac4 Compare September 22, 2026 02:22

@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: e6d9ac470b0e1aee0a8d5b8c99df56ccd69d0329(发布前刚重新读取远端 head;此前的 REQUEST_CHANGES 结论绑定在旧 head cd5bf1b19,本次 rebase 与修复后按流程重发)。

动机

这个 PR 的目标是把 grouped issue-fix Monitor 的「完整 bucket 集合 + add/observe/stop/reopen 决策」收敛到 TypeScript typed planner,Python 只保留 ledger IO、public writer 调用与投递,并在硬租约下为观察/停止取得自己的有界执行。它解决的不是表面问题:空 bucket 必须真的结束、membership 变化必须被观察、进程中断后同一执行必须能恢复、并且不能借用别人的执行。

上一轮 exact head 的结论是 REQUEST_CHANGES,阻塞点很窄但直接命中 PR 自己的承诺:attempt prefix 由 [goal_id, todo_id, actor, generated_at, cadence, rows] 派生,其中 generated_at 是每次命令的观察时间、rows 是原始行序。于是「acquire 之后进程退出、命令重跑」这种最正常的重试,只要时间前进一秒就会生成不同的 key,把自己的 active lease 当成别人的执行而拒绝(legacy 报 todo already has an active lease,file/SQLite 报 todo_lease_conflict),Monitor 被卡到 TTL 到期。上一轮的同一时间戳测试恰好掩盖了它。

本 head 已经修掉这个阻塞点,并在最新 main 上重做验证。

改动思路

分层没有变:planIssueFixMonitorReconciliation 从完整 ledger 与 canonical Todo 生成 typed steps,Python adapter 在 effect 前 acquire、拿到自己的 execution proof 后 re-plan、再调用 public writer,最后按 exact proof 释放自己的这次尝试;本轮修复只发生在 attempt identity 与 acquire 后的 re-plan 这两处:

  • _attempt_prefix:attempt 的身份改为「这次执行作用于哪个 reconciliation subject」——Goal、Monitor Todo、被观察的 bucket、actor。观察时间与 ledger 行序都不再进入身份;不同 subject 或不同 actor 铸出的 key 永远不会匹配这个前缀,因此「同 agent 也不许借用别人的执行」这条负例仍然成立。
  • _decision_signature:acquire 之后的 typed re-plan 仍然执行,但比较的是业务决策本身(operation、target、write scope、membership digest、material_change / completion evidence),而不是整份 step。观察时钟与它派生的 schedule 不再被当成 source change;而真正变化的决策(operation 变了、membership digest 变了、material-change 判定变了、write scope 变了)依旧会在任何 effect 之前中止。执行的是 re-plan 后重新确认过的那一步,所以恢复后的写入记录的是这次执行真正观察到的时间,而不是崩溃那次的时间。

同一份边界里,Python 侧被删除的旧实现(_active_grouped_monitors_existing_issue_fix_monitors_group_fingerprint 以及内联的 add/observe/complete 分支)是同一策略的第二份 owner;TS planner 把持久化身份约定(code-point 排序 + ensure_ascii 转义 + 16 位 hex 摘要)显式写出来并有专门测试钉住,所以既有 Monitor 的 membership digest 不会因为迁移而悄悄变化。这条「消除重复 owner」是本 PR 的主要价值之一。

具体改动

exact head 相对 origin/main@4c0a01744 改 16 个文件、+845/-236:生产代码 +331/-226(新的 TS planner 134 行、Python adapter 净减 45 行、todos.py 的 lease-proof fence 15 行、CLI 的 runtime-root 透传 1 行),文档 +81/-6,测试 +433/-4。

关键代码讲解

  1. planIssueFixMonitorReconciliation 用一个 discriminated step union 表达 add/observe/reactivate/complete/unchanged,拒绝冲突 bucket 身份、歧义 active target、非本命名空间的目标与「比持久化状态更旧」的观察;archive/superseded 历史不能遮蔽当前工作。它注册在既有 effect-runtime handler 表(effect_runtime_handlers.ts:428),没有新增 RPC。
  2. materialize_issue_fix_grouped_monitors 逐 bucket 执行:unchanged 只释放自己那次被中断的执行;add 走 public create;observe/stop 先 inspect → acquire(自己的 key)→ re-plan → public writer(带 lease proof)→ 按 exact proof 释放。每个 bucket 独立提交,后一个失败不回滚前一个。
  3. hold_task_lease_mutation_fence:显式 Monitor 观察现在必须带 active 的精确 lease proof 才能进入 Todo 事务(require_active_when_key_supplied=True),且该 fence 不退役执行——释放仍由获取者负责。这条改动是通用的 monitor/lease 语义,没有 issue-fix 专有措辞进入 core。
  4. CLI 把既有 --runtime-root 覆盖透传到 reads/leases/writes/projection,避免「读一个 authority、写另一个」的隐性分叉。
  5. 回归测试:三 provider 的 acquire 后中断 + 晚一秒 的 fresh invocation(同 key/epoch、成功恢复并释放)、ledger 行序反转但语义相同的恢复、unrelated execution 仍然被拒、真实子进程 commit 后退出再恢复清理、以及钉住 prefix/decision signature 语义的单元测试。

rebase 说明

已 rebase 到最新 main。冲突只在两处共享注册表:effect_runtime_handlers.ts(main 新增 periodic-report handler)与 authority_store_conformance.ts(main 新增 periodic-report conformance)。解决方式是两边都保留;rebase 后 typecheck 与各 provider 的 grouped-Monitor conformance 全部通过。

验证

  • pytest -q tests/capabilities/test_issue_fix_monitor_execution.py tests/capabilities/test_issue_fix_grouped_monitor_materialization.py42 passed(legacy/file/sqlite)。
  • 修复前后对照:修复前同一用例(重试时间 +1s)三 provider 全部红,报 todo_lease_conflict;修复后同 key/epoch 恢复并释放。
  • npm run typecheck:control-plane 通过;npm run test:control-plane2351 passed / 1 failed,唯一失败是 sqlite_capacity.test.ts 的 small-capacity entrypoint,且该失败在干净 main 上同样复现(与本 diff 无关)。
  • 真实 PostgreSQL 后端:临时目录拉起隔离的 PostgreSQL 16.15 实例(独立端口/库),postgresql_authority_store.integration.test.ts + postgresql_authority_service.integration.test.ts206 passed / 0 failed / 0 skipped;grouped-Monitor conformance 在 PostgreSQL / SQLite / no-KV 三套 provider 上都注册并通过。
  • ruff check tests loopx/canary loopx/control_plane loopx/domain_packs loopx/presentationmypygit diff --checkcanary premerge(19 条检查,0 failure,0 manual hold)通过。

对主干的风险

已关闭的阻塞点:attempt identity 依赖观察时钟导致「自己的重试冲突自己的 lease」。新 head 在三个 provider 上先红后绿,且保留 unrelated execution 必须被拒的负例。

语义与兼容性:既有 Monitor 的 target key、16 位 hex membership digest 约定与 Todo 元数据不变,无需状态迁移;write_performed 仍然只表示 Todo 业务写入,lease 清理与展示投递在文档里单独说明;--execute-transition 之外仍然只读。

需要维护者知道的两个有界权衡

  1. 身份改为 subject 之后,同 actor 对同一 Monitor 的并发调用与「重试」不可区分,因此并发者可能 adopt/release 在飞的 lease;此时 Todo 事务的 proof 校验会把它的写入变成一次明确失败,而不是重复业务写入。若将来需要严格的 per-invocation 互斥,需要引入持久化的 attempt token,而不是回到时钟。
  2. acquire 后的 re-plan 会重读 canonical Todo 状态,但仍复用本次 invocation 已加载的 ledger 行;所以「计划与取得租约之间 ledger 被改写」这一毫秒级窗口不会被重新观察(改动前同样如此,且并发提交被 writer 的 proof fence 挡住)。

证据边界:本地真实后端覆盖 legacy/File/SQLite 与隔离 PostgreSQL 16;生产 PostgreSQL 部署、容量与 soak 明确不在本 PR 范围。exact head 的远端 CI 在审阅时仍在跑(其中有独立 PostgreSQL job)。与本 diff 无关的既有失败另有两条:test_reward_memory_feedback_hint.py::test_generated_command_previews_reviewed_event_and_rejects_scope_expansion(Python,干净 main 上同样失败)与 sqlite_capacity.test.ts 的 small-capacity entrypoint(TS,干净 main 上同样失败)。

我的整体评价

方向正确且收敛:决策只有一份 owner(TS),Python 退回为真实 caller,重复的 Python 分组/指纹/转移实现被删除而不是并存;恢复语义、观察证据与 runtime-root 路由都有三 provider(含真实 PostgreSQL)证据。上一轮唯一阻塞点已经用可复现的「先红后绿」修复,负例(借用他人执行、过期/畸形证据、unchanged 静默)仍然成立。据此给出 APPROVE;按维护者授权进行 bypass 合并,并把上面两个有界权衡与 CI 状态如实记录。

English verdict: APPROVE - exact head e6d9ac4; the only blocking finding from the previous review (a lease attempt identified by the observation clock, which made an interrupted Monitor execution collide with its own lease on legacy/File/SQLite) is fixed by deriving the attempt from the reconciliation subject and comparing the post-acquire plan by decision, with a red-before/green-after three-provider regression, a re-ordered-ledger variant and the unrelated-execution negative case still passing; the PR moves the grouped Monitor decision into one typed TS planner, deletes the duplicate Python decision owner, routes the explicit runtime root, and is validated with 42 focused Python tests, 2351 passing TS tests (one pre-existing main failure), an isolated real PostgreSQL 16 run of 206 tests plus the provider conformance, typecheck, lint, mypy and the 19-check pre-merge gate; residual risk is the subject-derived identity's concurrency trade-off, the stale-ledger window between plan and acquisition, and CI pending at review time.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
A retry of an interrupted Monitor execution is a fresh invocation: it observes a
later time and may see a re-ordered ledger. The lease-attempt prefix was derived
from the observation timestamp and the raw ledger rows, so such a retry minted a
different key, refused to recognize the lease it already held, and collided with
it (`todo_lease_conflict`) until the TTL expired - the opposite of the recovery
this change set claims.

Identify the attempt by the reconciliation subject the actor executes against
(Goal, Monitor Todo, watched bucket, actor). A key minted for another subject or
by another actor still never matches, and the post-acquire typed re-plan still
validates the decision: the observation clock and the schedule it derives are
excluded from that comparison, while a changed operation, membership digest,
material-change verdict or write scope still stops the stale step. The
revalidated step is the one written, so a recovered execution records the time
it actually observed.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
@huangruiteng
huangruiteng force-pushed the codex/authority-execution-closure branch from e6d9ac4 to 1865708 Compare September 22, 2026 02:45

@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: 186570880352d0766317fe7bd504aa5f43b04dfe(发布前刚重新读取远端 head;此前的 REQUEST_CHANGES 结论绑定在旧 head cd5bf1b19,本次 rebase 与修复后按流程重发)。

动机

这个 PR 的目标是把 grouped issue-fix Monitor 的「完整 bucket 集合 + add/observe/stop/reopen 决策」收敛到 TypeScript typed planner,Python 只保留 ledger IO、public writer 调用与投递,并在硬租约下为观察/停止取得自己的有界执行。它解决的不是表面问题:空 bucket 必须真的结束、membership 变化必须被观察、进程中断后同一执行必须能恢复、并且不能借用别人的执行。

上一轮 exact head 的结论是 REQUEST_CHANGES,阻塞点很窄但直接命中 PR 自己的承诺:attempt prefix 由 [goal_id, todo_id, actor, generated_at, cadence, rows] 派生,其中 generated_at 是每次命令的观察时间、rows 是原始行序。于是「acquire 之后进程退出、命令重跑」这种最正常的重试,只要时间前进一秒就会生成不同的 key,把自己的 active lease 当成别人的执行而拒绝(legacy 报 todo already has an active lease,file/SQLite 报 todo_lease_conflict),Monitor 被卡到 TTL 到期。上一轮的同一时间戳测试恰好掩盖了它。

本 head 已经修掉这个阻塞点,并在最新 main 上重做验证。

改动思路

分层没有变:planIssueFixMonitorReconciliation 从完整 ledger 与 canonical Todo 生成 typed steps,Python adapter 在 effect 前 acquire、拿到自己的 execution proof 后 re-plan、再调用 public writer,最后按 exact proof 释放自己的这次尝试;本轮修复只发生在 attempt identity 与 acquire 后的 re-plan 这两处:

  • _attempt_prefix:attempt 的身份改为「这次执行作用于哪个 reconciliation subject」——Goal、Monitor Todo、被观察的 bucket、actor。观察时间与 ledger 行序都不再进入身份;不同 subject 或不同 actor 铸出的 key 永远不会匹配这个前缀,因此「同 agent 也不许借用别人的执行」这条负例仍然成立。
  • _decision_signature:acquire 之后的 typed re-plan 仍然执行,但比较的是业务决策本身(operation、target、write scope、membership digest、material_change / completion evidence),而不是整份 step。观察时钟与它派生的 schedule 不再被当成 source change;而真正变化的决策(operation 变了、membership digest 变了、material-change 判定变了、write scope 变了)依旧会在任何 effect 之前中止。执行的是 re-plan 后重新确认过的那一步,所以恢复后的写入记录的是这次执行真正观察到的时间,而不是崩溃那次的时间。

同一份边界里,Python 侧被删除的旧实现(_active_grouped_monitors_existing_issue_fix_monitors_group_fingerprint 以及内联的 add/observe/complete 分支)是同一策略的第二份 owner;TS planner 把持久化身份约定(code-point 排序 + ensure_ascii 转义 + 16 位 hex 摘要)显式写出来并有专门测试钉住,所以既有 Monitor 的 membership digest 不会因为迁移而悄悄变化。这条「消除重复 owner」是本 PR 的主要价值之一。

具体改动

exact head 相对 origin/main@4c0a01744 改 16 个文件、+845/-236:生产代码 +331/-226(新的 TS planner 134 行、Python adapter 净减 45 行、todos.py 的 lease-proof fence 15 行、CLI 的 runtime-root 透传 1 行),文档 +81/-6,测试 +433/-4。

关键代码讲解

  1. planIssueFixMonitorReconciliation 用一个 discriminated step union 表达 add/observe/reactivate/complete/unchanged,拒绝冲突 bucket 身份、歧义 active target、非本命名空间的目标与「比持久化状态更旧」的观察;archive/superseded 历史不能遮蔽当前工作。它注册在既有 effect-runtime handler 表(effect_runtime_handlers.ts:428),没有新增 RPC。
  2. materialize_issue_fix_grouped_monitors 逐 bucket 执行:unchanged 只释放自己那次被中断的执行;add 走 public create;observe/stop 先 inspect → acquire(自己的 key)→ re-plan → public writer(带 lease proof)→ 按 exact proof 释放。每个 bucket 独立提交,后一个失败不回滚前一个。
  3. hold_task_lease_mutation_fence:显式 Monitor 观察现在必须带 active 的精确 lease proof 才能进入 Todo 事务(require_active_when_key_supplied=True),且该 fence 不退役执行——释放仍由获取者负责。这条改动是通用的 monitor/lease 语义,没有 issue-fix 专有措辞进入 core。
  4. CLI 把既有 --runtime-root 覆盖透传到 reads/leases/writes/projection,避免「读一个 authority、写另一个」的隐性分叉。
  5. 回归测试:三 provider 的 acquire 后中断 + 晚一秒 的 fresh invocation(同 key/epoch、成功恢复并释放)、ledger 行序反转但语义相同的恢复、unrelated execution 仍然被拒、真实子进程 commit 后退出再恢复清理、以及钉住 prefix/decision signature 语义的单元测试。

rebase 说明

已 rebase 到最新 main。冲突只在两处共享注册表:effect_runtime_handlers.ts(main 新增 periodic-report handler)与 authority_store_conformance.ts(main 新增 periodic-report conformance)。解决方式是两边都保留;rebase 后 typecheck 与各 provider 的 grouped-Monitor conformance 全部通过。 之后 main 又落的两次提交(研究文档补充 + stage2c mutant oracle 稳定性修复)与本 PR 文件无重叠,已无冲突同步;本 head 与 origin/main 的 diff 仍是 16 个文件、+845/-236,Lark/issue-fix 聚焦套件在最终 head 上重跑通过。

验证

  • pytest -q tests/capabilities/test_issue_fix_monitor_execution.py tests/capabilities/test_issue_fix_grouped_monitor_materialization.py42 passed(legacy/file/sqlite)。
  • 修复前后对照:修复前同一用例(重试时间 +1s)三 provider 全部红,报 todo_lease_conflict;修复后同 key/epoch 恢复并释放。
  • npm run typecheck:control-plane 通过;npm run test:control-plane2351 passed / 1 failed,唯一失败是 sqlite_capacity.test.ts 的 small-capacity entrypoint,且该失败在干净 main 上同样复现(与本 diff 无关)。
  • 真实 PostgreSQL 后端:临时目录拉起隔离的 PostgreSQL 16.15 实例(独立端口/库),postgresql_authority_store.integration.test.ts + postgresql_authority_service.integration.test.ts206 passed / 0 failed / 0 skipped;grouped-Monitor conformance 在 PostgreSQL / SQLite / no-KV 三套 provider 上都注册并通过。
  • ruff check tests loopx/canary loopx/control_plane loopx/domain_packs loopx/presentationmypygit diff --checkcanary premerge(19 条检查,0 failure,0 manual hold)通过。

对主干的风险

已关闭的阻塞点:attempt identity 依赖观察时钟导致「自己的重试冲突自己的 lease」。新 head 在三个 provider 上先红后绿,且保留 unrelated execution 必须被拒的负例。

语义与兼容性:既有 Monitor 的 target key、16 位 hex membership digest 约定与 Todo 元数据不变,无需状态迁移;write_performed 仍然只表示 Todo 业务写入,lease 清理与展示投递在文档里单独说明;--execute-transition 之外仍然只读。

需要维护者知道的两个有界权衡

  1. 身份改为 subject 之后,同 actor 对同一 Monitor 的并发调用与「重试」不可区分,因此并发者可能 adopt/release 在飞的 lease;此时 Todo 事务的 proof 校验会把它的写入变成一次明确失败,而不是重复业务写入。若将来需要严格的 per-invocation 互斥,需要引入持久化的 attempt token,而不是回到时钟。
  2. acquire 后的 re-plan 会重读 canonical Todo 状态,但仍复用本次 invocation 已加载的 ledger 行;所以「计划与取得租约之间 ledger 被改写」这一毫秒级窗口不会被重新观察(改动前同样如此,且并发提交被 writer 的 proof fence 挡住)。

证据边界:exact head 上 test-shard (4) 报的 test_project_lifecycle_goal_channel::test_refresh_state_dispatches_and_replays_post_writeback_sidecar 是既有失败(干净 main 上本地复现,报错在 loopx/control_plane/quota/settlement.py),与本 diff 无关;其余 shard 与 PostgreSQL job 在审阅时仍在跑。本地真实后端覆盖 legacy/File/SQLite 与隔离 PostgreSQL 16;生产 PostgreSQL 部署、容量与 soak 明确不在本 PR 范围。exact head 的远端 CI 在审阅时仍在跑(其中有独立 PostgreSQL job)。与本 diff 无关的既有失败另有两条:test_reward_memory_feedback_hint.py::test_generated_command_previews_reviewed_event_and_rejects_scope_expansion(Python,干净 main 上同样失败)与 sqlite_capacity.test.ts 的 small-capacity entrypoint(TS,干净 main 上同样失败)。

我的整体评价

方向正确且收敛:决策只有一份 owner(TS),Python 退回为真实 caller,重复的 Python 分组/指纹/转移实现被删除而不是并存;恢复语义、观察证据与 runtime-root 路由都有三 provider(含真实 PostgreSQL)证据。上一轮唯一阻塞点已经用可复现的「先红后绿」修复,负例(借用他人执行、过期/畸形证据、unchanged 静默)仍然成立。据此给出 APPROVE;按维护者授权进行 bypass 合并,并把上面两个有界权衡与 CI 状态如实记录。

English verdict: APPROVE - exact head 1865708; the only blocking finding from the previous review (a lease attempt identified by the observation clock, which made an interrupted Monitor execution collide with its own lease on legacy/File/SQLite) is fixed by deriving the attempt from the reconciliation subject and comparing the post-acquire plan by decision, with a red-before/green-after three-provider regression, a re-ordered-ledger variant and the unrelated-execution negative case still passing; the PR moves the grouped Monitor decision into one typed TS planner, deletes the duplicate Python decision owner, routes the explicit runtime root, and is validated with 42 focused Python tests, 2351 passing TS tests (one pre-existing main failure), an isolated real PostgreSQL 16 run of 206 tests plus the provider conformance, typecheck, lint, mypy and the 19-check pre-merge gate; residual risk is the subject-derived identity's concurrency trade-off, the stale-ledger window between plan and acquisition, and CI pending at review time.

@huangruiteng
huangruiteng merged commit 3a419ab into main Sep 22, 2026
22 of 23 checks passed
@huangruiteng
huangruiteng deleted the codex/authority-execution-closure branch September 22, 2026 02:51
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