fix(restore): 机器重启后托管会话不再被误判僵尸清除——missing 一律保留 lazy cold-resume - #705
Open
deepcoldy wants to merge 1 commit into
Open
fix(restore): 机器重启后托管会话不再被误判僵尸清除——missing 一律保留 lazy cold-resume#705deepcoldy wants to merge 1 commit into
deepcoldy wants to merge 1 commit into
Conversation
## 问题 机器重启后,重启前正活跃的飞书会话无法自动 resume。根因:默认 tmux 后端的 bmx-* 会话与 operator 自己终端共享默认 tmux socket。reboot 后 operator 终端先于 daemon restore 复活了共享 server,导致 restore 里的 `serverState()==='down'`(本用于区分「整机重启」vs「单 pane 僵尸」)判成 `'running'` → 每个 missing 的 pane 被误判成 solo zombie → closeSession 永久关闭。live 日志实测:一次 reboot 关掉 239 个活跃会话,仅 1 个 zellij (每会话独立 server)逃过。 ## 修复(方案 B:托管会话永不自动僵尸关闭) restore 探到持久后端 backing `missing` 时不再 close,一律保留 worker-less active 记录,交给下条消息 cold-resume(forkWorker(resume=true);transcript 真没了则 worker 侧 resume→fresh 兜底新起干净会话 + 用户可见提示,永不卡死)。 这让 tmux/zellij 对齐既有的 pty(无持久后端从不探测)与 zmx(missing 恒 keep)行为。 判断依据不再依赖「共享 server 是否存活」这个被 co-tenant 污染的信号,从根本 上消除竞态。离开 active 集合的仅剩三种:真 `/close`(restore 前就已置 closed,根本不进本分支)、adopt 目标已退出(adopt 分支处理)、CLI 配置切换 mismatch(下方分支处理,与 backing 存活正交)。 ## 影响面 - 跨后端:tmux/zellij 行为改变(不再 close-on-missing);zmx/pty 本就如此, 无变化;herdr 之前走 serverState='unknown'→close,现在同样保留。 - 死代码清理:移除 `probePersistentBackendServer()` 及 TmuxBackend/ ZellijBackend/ZmxBackend 三个 `serverState()`(本就是为这个被推翻的 reboot-gate 而加,现零调用;非 backend 接口契约成员,删除安全)。 - 未新增 TTL 回收:dormant 会话无 worker、无 pane,仅剩磁盘行 + dashboard 行,近零资源占用,无需回收(如需 dashboard 整洁另按 lastMessageAt 解耦处理)。 ## 验证 - pnpm build 通过(tsc + dashboard bundle + audit 全绿) - test/restore-zombie-close.test.ts:missing→close 断言翻转为 missing→keep, 新增 reboot 全保留 + cap-suspend 保留覆盖(25 tests 绿) - test/tmux-probe.test.ts 去掉 serverState 块(7 绿)、session-resume.test.ts 去掉 serverState stub(33 绿) - 全量 vitest run 通过(exit 0,无 fail marker)
Owner
Author
|
@codex 请复审本 PR(head 钉 一句话:修机器重启后飞书会话不自动 resume——共享默认 tmux socket 被 operator 终端复活,骗过 建议重点核:
全量 vitest exit 0; |
|
To use Codex here, create a Codex account and connect to github. |
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.
问题
机器重启后,重启前正活跃的飞书会话无法自动 resume——它们被永久
closeSession了,话题从看板消失、下条消息也不再 lazy-resume。根因:默认 tmux 后端的
bmx-*会话与 operator 自己终端共享默认 tmux socket(ensure-tmux.ts注释明说 "Production intentionally targets the default server")。daemon:restart正常:tmux server 独立于 daemon 存活 → pane 还在 → 重连活着的 CLI。TmuxBackend.serverState()返回'running'而非'down'→ 守卫被击穿 → 每个missing的 pane 被误判成 solo zombie →closeSession永久关闭。live 日志实测(一次真实 reboot):僵尸关闭 239 个活跃会话,正确「server down 保留」仅 1 个(还是 zellij——每会话独立 server 才没被共享 socket 骗到)。最讽刺的是被杀的都是正在用的活跃会话,而闲置被内存回收挂起(
suspendedColdResume)的老会话反而走另一分支活了下来。修复(方案 B:托管会话永不自动僵尸关闭)
restore 探到持久后端 backing
missing时不再 close,一律保留 worker-less active 记录,交给下条消息 cold-resume:forkWorker(resume=true);若 transcript 真没了,worker 侧已有的 resume→fresh 兜底(worker.tstier-1/tier-2)会自动新起干净会话 + 发用户可见提示「历史会话无法恢复,已新起」——永不卡死。这让 tmux/zellij 对齐既有的 pty(无持久后端,从不探测/关闭)与 zmx(
missing恒 keep)行为。判断依据不再依赖「共享 server 是否存活」这个被 co-tenant 污染的信号,从根本上消除竞态。离开 active 集合的仅剩三种,均与 backing 存活正交、逻辑不变:
/close——restore 前就已置status:'closed',根本不进这个active分支;closeActiveSessionIfCliMismatch处理。为什么不需要 TTL 回收
dormant 会话无 worker、无 pane,仅剩磁盘一行 + dashboard 一行,近零资源占用(申晗确认)。故不引入 TTL。若将来需要 dashboard 整洁,应另按
lastMessageAt做解耦的 dormancy 回收,与本 bug 无关。影响面(多 CLI × 多后端横向排查)
serverState返回'unknown'→ 落到 close 分支;现在同样保留(更安全,且 herdr 本就有 warm-up 慢启动,之前那条路径反而可能误杀)。probePersistentBackendServer()及TmuxBackend/ZellijBackend/ZmxBackend三个serverState()静态方法。它们本就是 commitb260f405为这个被推翻的 reboot-gate 而加的,现零调用;非 backend 接口契约成员(不在types.ts/capabilities.ts),删除安全。保留只会诱使未来有人把坏 gate 重新接回来。验证
pnpm build通过(tsc + dashboard bundle + audit 全绿)。test/restore-zombie-close.test.ts:missing→close断言翻转为missing→keep;新增/改写「reboot 全保留」「cap-suspend 保留」覆盖;删除已失效的serverState脚手架与「unknown→保守 close」用例(现 unknown 也 keep)。25 tests 绿。test/tmux-probe.test.ts:删除serverStatedescribe 块(7 绿)。test/session-resume.test.ts:删除死serverStatestub(33 绿);其原有「不 zombie-close adopt/woken 会话」断言在新行为下更强、仍绿。vitest run通过(exit 0,无 fail marker)。净 diff -122 行:修复方式是移除被击穿的机器,而非再加一层守卫。
base=master
fe594fc8b(直接基于,merge-base 干净)· head1190abea1