refactor(session): 开场激活的分散串行化 fence 收进按 session 的命令队列(Stage 3) - #1308
Merged
Conversation
同一 sessionId 上的命令跨 await 后仍需串行。此前这个保证由开场激活窗口里 一组互相配合的机制拼出来:到达时预留 FIFO 序号、AdmissionsOutstanding 计数、 ReleasePending 延迟交接、settle 时回放释放、100ms 重试定时器。它们存在的唯一 原因是「到达 → 构造 prompt(await 发送者查询)→ 落 durable tail」中间可能被 后到的消息或 worker 的开场 ACK 插队。 - 新增 core/session-turn-queue.ts:按 sessionId 的 Promise 链,命令按入队顺序 执行、跨自身 await 不交错,前一条失败不阻塞后一条,链空即回收。不引入 mailbox / actor 类型。 - daemon 两处 follower 入口(initialStartPending 下的同 anchor 后到消息、 worker 已死带 retained journal 的 refork 前置 staging)统一走 admitFollowerBehindOpening:到达时同步取序号,构造 + 落盘作为一条命令 入队;若开场的释放已先跑完(tail 空、路由已放开),同一条命令内联 promote。 pendingRepo 分支从到达到落盘没有 await,直接同步落盘。 - 开场路由释放 releaseQueuedActivationReservation(worker ACK、普通冷 fork 后 的交接、失败重试)入队,因此必然在先到的 follower 落盘之后执行; worker-pool 的 onQueuedActivationSubmitted 回调相应变为异步。 - hasQueuedActivationAdmissionGate 改查队列:有命令在队列上时 live worker 的 普通 turn 也进 durable tail,不得插队。 - 删除 queuedActivationTailAdmissionsOutstanding、 queuedActivationTailReleasePending、reserveAsync/settleAsync 及其回放; 保留的重试定时器只负责 promote 落盘 / IPC 失败后重试。 - 删除从未被写入的 pendingQueuedActivationFollowUps 与 reparkQueuedActivationFollowUpTail(#597 durable tail 落地后 repark 恒返回 false)。 - 设计文档:Stage 3 标为已落地并列出有意保留项(fork 边界的 claim、 pendingRepo 等待期缓冲、admit/promote 的回滚写法、worker exit 路径); Stage 4 降为低优先级、暂不立项;Stage 0 / Stage 1 收尾按升级窗口已关闭 排期,范围清单写入 §5。
重试定时器不能只看 initialStartPending:ACK 已清路由时,promote 落盘失败 仍要把 durable tail 送出去,不能依赖 promote 在抛错前顺手改回 pending。 Co-authored-by: Cursor <cursoragent@cursor.com>
Bun 的 ReadStream 没有 unref(),destroy()+unref 是空操作;afterAll 的 kill-server 超时只发 SIGTERM。两条都过了之后进程仍不退出,CI bun-test 就被 per-file wall SIGKILL。teardown 改走 close(),清理用 SIGKILL, runner 在出结果后空闲一个 test timeout 即收掉残留 handle。 Co-authored-by: Cursor <cursoragent@cursor.com>
LucasIcarus
force-pushed
the
feat/session-turn-queue
branch
from
September 8, 2026 05:19
29d659f to
86b051a
Compare
LucasIcarus
marked this pull request as ready for review
September 8, 2026 05:20
cfc425a 仍是当前 master 祖先,只是过期。改记 origin/master@61dadb04c。 Co-authored-by: Cursor <cursoragent@cursor.com>
deepcoldy
approved these changes
Sep 8, 2026
deepcoldy
left a comment
Owner
There was a problem hiding this comment.
双审通过(0 阻断)。
首审 + 复审两轮结论一致:
- 队列本体逐行核过;反变异 7 枪 5 红,2 绿均插桩定性为非缺陷(惰性编辑 / 下游在抛错前置真 flag)。
- 无回归:本地全量两次 vs 干净 master 同负载对照,「两次都失败」∩「master 不失败」= 空集。
- 自等死锁风险已排查:生产侧 2 个入队点 / 3 个查队列点全部核过调用上下文,无同 session 嵌套路径。
- 必需 check(build / test)全绿;bun-test 1106/1106 files green。
- 非阻断的设计文档 baseline 已由作者自行修正(f83c1ca2b)。
说明:github-advanced-security 这条红与代码无关——日志根因是 You are not licensed to use Copilot(403 / errorType: authentication),且该 check 不在必需列表(ruleset 只要求 build + test)。
This was referenced Sep 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
改了什么
设计文档
docs/design/2026-08-12-session-restage-store-first.md§3 Stage 3:同一sessionId上的命令跨await后仍要串行。此前这个保证由开场激活窗口里一组互相配合的机制拼出来:到达时预留 FIFO 序号、queuedActivationTailAdmissionsOutstanding计数、queuedActivationTailReleasePending延迟交接、settle 时回放释放、100ms 重试定时器。它们存在的唯一原因是「到达 → 构造 prompt(await发送者查询)→ 落 durable tail」中间可能被后到的消息或 worker 的开场 ACK 插队。本 PR 用一条按 session 的命令队列取代这组机制:
src/core/session-turn-queue.ts:runSessionTurn(sessionId, command)是按sessionId的 Promise 链,命令按入队顺序执行、跨自身await不交错,前一条失败不阻塞后一条,链空即回收;hasPendingSessionTurns(sessionId)供入口判定。不引入 mailbox / actor 类型。initialStartPending下的同 anchor 后到消息;worker 已死、带 retained journal 的 refork 前置 staging)统一走admitFollowerBehindOpening:到达时同步取序号,构造 + 落盘作为一条命令入队。若开场的释放已先跑完(tail 空、路由已放开),同一条命令内联 promote,不再排队。pendingRepo 分支从到达到落盘没有await,直接同步落盘。releaseQueuedActivationReservation(workerqueued_activation_submittedACK、普通冷 fork 后的交接、失败重试)入队,因此必然在先到的 follower 落盘之后执行;worker-pool 的onQueuedActivationSubmitted回调相应变为异步(回调类型本来就允许 Promise)。hasQueuedActivationAdmissionGate改查队列:队列上有命令时,live worker 的普通 turn 也进 durable tail,不得插队;forkReservedInitialSession与原始命令冷启动对计数的判定同样改为查队列。queuedActivationTailAdmissionsOutstanding、queuedActivationTailReleasePending、reserveAsync… / settleAsync…及其回放和测试钩子。保留的重试定时器只负责 promote 落盘 / IPC 失败后的重试,不再承担排序。pendingQueuedActivationFollowUps与reparkQueuedActivationFollowUpTail:fix(codex-app): make turn ownership and recovery durable #597 落地 durable tail 之后没有任何写入方,repark 恒返回 false,worker exit / error 里对它的调用也一并删除。有意保留、没有入队的
initialStartClaimToken/initialStartPending:fork 边界的所有权,跨越整段资源准备(秒级)。期间到达的 follower 必须立刻落 durable tail 才扛得住 daemon 崩溃,把整段准备做成队列命令会让 follower 在内存里等待。它是状态,不是await间隙。pendingFollowUps*缓冲:等人点卡片,队列不能被人拿着。admitQueuedActivationTail/promoteQueuedActivationTail里「store 外备份再回滚」的写法:删除条件不变(daemon 侧出现按命令更新且不替换ds.session引用的 apply 入口);让 admit 走它需要新的 store 导出并改多个 stub 了updateSession的测试夹具,本轮未做。updateSession:没有复现证据,按文档「没有复现的路径不改」不动。设计文档
mojo-containment的 revoke 安全闸仍直接扫 JSON、SQLite 后静默失效这一条),作为下一个 PR。影响面
daemon.ts的handleThreadReplyAdmitted/handleNewTopic两处 follower 入口、forkReservedInitialSession、forkReservedInitialRawSession、release 回调)与worker-pool.ts的 admission gate 和 worker exit / error 处理;不动 session-store、CLI、dashboard、沙盒。forkReservedInitialSession立即释放且失败不重试、延迟释放才重试;现在两种情况统一走队列并在失败时重试。验证
bun run build绿,tsc --noEmit绿。test/session-turn-queue.test.ts(5 条:入队顺序、跨 session 不互斥、失败不阻塞、pending 判定、链回收)。test/daemon-rename-route.test.ts里三条原本对计数/延迟交接断言的用例改为对队列顺序断言(ACK 落在 follower 构造中、ACK 后 worker 退出、follower 落盘失败),新增两条:开场释放先于 follower 跑完时 follower 内联 promote;follower 构造期间 admission gate 保持关闭。session-lifecycle-start、worker-ready-display-mode、trigger-session-*、session-resume、dashboard-create-session等)1029 条全过。vitest run --project unit:1266 个文件通过、7 个跳过;21902 条通过、108 条跳过,0 失败。真机验证
bun run switch:here && bun run daemon:restart:四个 bot 由新进程持有 occupancy 租约,活跃会话正常恢复,日志无 ERROR。