Skip to content

fix(chat): finish atomic stop and queued Turn delivery - #7505

Merged
loopx-agent merged 9 commits into
mainfrom
codex/release220-chat-stop-integration-20261028
Oct 27, 2026
Merged

loopx-agent merged 9 commits into
mainfrom
codex/release220-chat-stop-integration-20261028

Conversation

@loopx-agent

@loopx-agent loopx-agent commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Maintainer delivery and provenance

This maintainer-owned release proposal completes the work from #7477. Original commits and DCO authorship by @JasonBuildAI are retained, together with the claim and expiry findings and their public exact-head resolution review. Maintainer follow-through adds expiry/acceptance serialization and independent recovery/order tests.

The original fork PR still has a truthful ready=false: its final repair pusher and approver are the same account, while GitHub requires last-push approval by another account. Its review, discussion and native readiness observation are preserved. This proposal explicitly uses the maintainer-authorized author-owned self-review/admin-delivery path, with a new complete exact-head review, qualification and readiness decision; it does not claim an independent second reviewer or change repository approval rules. #7477 will be marked superseded only after this proposal actually merges.

Problem and resulting behavior

A stop request racing completion could append stop intent to a Turn that had already completed, suppressing legitimate unfinished-work recovery. The store now checks the current status, persists admitted intent, and enters interrupting under the existing Turn lock. A terminal loser leaves the ledger unchanged; intent still precedes status for crash safety.

Attached-host queued claims, queue expiry and prepared acceptance settlement share the same Turn serialization boundary and re-read current state. They preserve successful claim identity and cannot overwrite a stop from an older queue snapshot. This resolves the claim-authority blocker in reviews #5666309659 and #5670407863, independently reproduced during maintainer review. No new execution permission, state schema, model policy or UI is added.

Closes #7476.

Validation

Exact head: c184163f8cf9534c24f8e5be2a13f153f98e59b5.

  • Seven complete local Chat/store/queue/recovery/presentation modules: 265 passed on this integrated head, zero skipped. This includes terminal loser, interrupt-first claim/expiry, expiry-first terminal-reason preservation through the public claimant and Runtime stop, claim-first identity preservation and crash after durable intent before status write.
  • The pre-fix expiry counterexample at 7fdf3cade6dd51900bcfa6f43cdb60d76bc73447 failed and remains in the final regression suite.
  • Final risk-based premerge, required Ruff, configured mypy, diff hygiene and isolated changed-path boundary passed. CI is not consulted under the release Goal's local-validation policy.
  • Actual local JSON, locks, sessions and event ledgers are exercised with synthetic Host adapters. Windows and live remote-model cancellation are not qualified by these results.
  • The same measured module workload is chat_store.py 2,003 -> 2,066 lines and chat_runtime.py 2,319 -> 2,320. The ceilings settle the reviewed atomicity work in this diff; no safety clause, authority field or unique regression assertion is removed to fit an earlier size.

Frontend / Visual Evidence

UI impact: none. Existing stop and queue interfaces retain their caller contract; installed release/UI qualification is separate.

JasonBuildAI and others added 9 commits October 27, 2026 22:47
interrupt_turn reads the Turn, appends the durable interrupt-intent event,
and only then compare-and-sets the status. When the Turn completed in
between, the CAS refuses the change but the intent event remains in the
completed Turn's ledger, because #7191 moved the intent ahead of the status
write without keeping the terminal-state check that write used to provide.
That breaks the terminal event history pinned by
test_interrupt_does_not_overwrite_a_completed_turn and makes the completed
Turn report stop_observed, which suppresses the unfinished-work
continuation in the external-conversation planner.

Make the status check, the intent event and the status write one store
transition under the Turn write lock. The intent still lands before the
status change, so a crash cannot turn a requested stop into an automatic
review, and a Turn that already reached a terminal state can no longer gain
a stop event after its terminal outcome.

Fixes #7476

Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
The stop-intent transition adds 33 lines to chat_store.py and one net line to chat_runtime.py. Refresh the reviewed module metric ceilings in the same diff so the maintainability ratchet settles the accepted growth.

Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
`claim_next_queued_turn` wrote the Turn from the snapshot the queue scan had
read, under the Session lock only, while `begin_turn_interrupt` read and
wrote the same Turn under the Turn write lock. The two writers therefore did
not share a serialization boundary: an interrupt that read `queued` could
resume after the claim had committed `running` with a `host_claim_id` and
had published the Session active pointer, then write its older snapshot back
as `interrupting` with no claim id.

Take the Turn write lock for the claim too, and re-read the Turn inside it.
An interrupt that already committed now wins the transition, so the claim
returns `None` instead of starting a Turn the stop request owns, and a claim
that commits first is never rewritten from a snapshot that predates it. The
queue scan stays a candidate list; the authoritative status check moves
under the lock that the interrupt also takes.

Cover the interleaving deterministically: the interrupt is paused after its
read, the claim is attempted while the interrupt still holds the lock, and
the test asserts the persisted status and claim id, the Session active
pointer and the event ledger agree.

Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
…to codex/review-chat-interrupt-7477-20261027

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
…lease220-chat-stop-integration-20261028

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

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

Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; reasoning_effort=max
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

APPROVE,精确 head c184163f8cf9534c24f8e5be2a13f153f98e59b5。本轮独立复审并修复了旧评审指出的claim竞争,以及复审新发现的expiry竞争。

动机 / Motivation

在Chat、桌面或Lark会话停止一个正在结束或排队的Turn的用户与宿主。 停止请求输给已完成Turn时仍追加stop intent,使未取消的工作被当作用户取消;早期修复又可能用旧快照抹掉成功领取的host_claim_id。 同一Turn锁内复查状态、先持久化停止意图再变更状态,终态败者不追加事件;领取、排队超时和acceptance结清共享该锁并重新读取当前状态。 不授予新的执行或自动继续权限,不改变宿主取消API、队列顺序、模型、配额或用户界面。

改动思路 / Approach

本次是维护者接管后的完整发布提案,保留 @JasonBuildAI 原始commit/DCO、#7477讨论与两个评审finding。原fork PR因最后pusher同reviewer,GitHub仍REVIEW_REQUIRED、native ready=false;该事实未被改写。这里按维护者明确授权的作者自评审路径,重新验证当前整合head的265项、premerge和静态/边界检查,声明为自评审,不冒充独立二审,也不修改GitHub规则。原PR只能在本提案实际合入后标为superseded。

复用ChatSessionStore原有Turn文件锁、事件账本及状态集合,将同一Turn的竞争写入纳入现有owner,不另建事务框架或Python/TS决策副本。 本PR修复既有Chat停止路径及同一store中直接竞争的queued writer,保留原宿主与恢复责任。 不是把事件移回状态写之后,而是把最新状态检查、durable intent和状态写入收在同一Turn锁内;这样终态败者无新增事件,崩溃时已接受的停止意图仍先于状态存在。相邻queued claim、expiry和acceptance结清也必须复用此边界,否则只锁interrupt仍会读旧快照覆盖执行归属。锁顺序保持Session后Turn,interrupt持Turn期间不反向取得Session。

具体改动 / Changes

逐项按 #7476 的 2026-10-27T09:11:31Z 版本核对 I7476-TERMINAL-LOSER、I7476-INTENT-ORDER、I7476-ATOMIC-TURN-TRANSITION:ChatRuntimeController.interrupt_turn调用store helper;begin_turn_interrupt在原Turn文件锁内重新读取,终态直接返回,允许状态才append intent并写interrupting。queued host claim锁内重新读取,只有仍queued才能提交claim并发布Session active pointer;claim先提交时完整claim字段保留,interrupt先赢时不领取其他queued头。排队expiry也锁内重读,不能在已接受停止后写timed_out/turn.failed;prepared acceptance结清仅在同锁内去掉capsule,保留当前Turn字段。

旧 CHANGES_REQUESTED #5666309659 的claim blocker已逐项核对:原来成功claim后的host_claim_id可能被旧queued快照抹掉,现在两个writer共享Turn锁,控制线程测试同时断言persisted status、claim、Session pointer和event ledger。新评审 #5670407863 的expiry blocker也逐项核对:我方另以7fdf3cade6dd独立反例实际复现expiry额外追加turn.failed,再修复并保留该例;另经真实claim_next_queued_turn让expiry先提交,再从Runtime stop,完整timed_out/session_queue_expired、claim/Session和ledger保持不变。claim先赢与intent后状态写前崩溃两例核验claim identity和原intent顺序。最终head七个完整模块265通过、0跳过;终态败者、interrupt先赢、claim先赢、intent后状态写前崩溃及排队超时均覆盖。先前7fdf3cade6dd的expiry反例1失败1通过,最终修复保留该反例。最终premerge、规定Ruff、mypy和四文件隔离边界通过。 模块大小在相同base/head测得chat_store 2003→2066、chat_runtime 2319→2320;只结清实测增长,不删锁、字段或安全语义来满足旧上限。

对主干的风险 / Risks

主要风险是锁序反转或把旧候选当作最新authority。逐条检查了Session→Turn顺序、lock内重新读取与终态返回;没有Turn→Session的新增持锁路径,事件仍走原账本owner。265例覆盖实际本地JSON、文件锁、队列、进程恢复及原UI队列后端,适配器仍为合成Host,不能把这些结果称为真实模型取消或Windows资格。本机真实JSON存储、文件锁和事件账本已验证;适配器使用合成Host,未执行Windows或真实远端模型取消。发布版完整安装与UI旅程由统一候选完成。 CI依Goal策略未查询,先前失败保持历史失败,只有最终source的通过支持当前结论。

我的整体评价 / Verdict

复用ChatSessionStore原有Turn文件锁、事件账本及状态集合,将同一Turn的竞争写入纳入现有owner,不另建事务框架或Python/TS决策副本。 本PR修复既有Chat停止路径及同一store中直接竞争的queued writer,保留原宿主与恢复责任。 三项issue验收和旧claim blocker在当前head都有代码与独立执行证据;same-owner companion修复关闭了expiry的实际反例,acceptance结清也不再越过Turn锁。没有新增通用事务框架、影子状态或外部权限。当前改动可以独立回滚,无状态迁移;仍需在正式发布候选完成完整安装和渠道核验。发布APPROVE后只按原生closeout核对已解决旧review,未核验的异议不会被删除或忽略。

English verdict: APPROVE c184163f8cf9534c24f8e5be2a13f153f98e59b5. The terminal loser adds no stop intent; admitted intent stays durable before status; claim,expiry andacceptance settlement serialize onthe sameTurnlock.265cases andrequiredlocalgates passed. Priorclaim blocker is resolved;realremoteHost/Windows qualification isnotclaimed.

@loopx-agent
loopx-agent merged commit 085d9a7 into main Oct 27, 2026
3 of 5 checks passed
@loopx-agent
loopx-agent deleted the codex/release220-chat-stop-integration-20261028 branch October 27, 2026 16:36
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.

[Bug]: a stop request racing Turn completion writes turn.interrupt_requested after turn.completed and suppresses unfinished-work recovery

2 participants