Skip to content

fix(ui-mac): close active connections on token-only rotation stop - #911

Merged
jinjunnn merged 2 commits into
alphafrom
feat/858-token-rotation-close-connections
Aug 11, 2026
Merged

jinjunnn merged 2 commits into
alphafrom
feat/858-token-rotation-close-connections

Conversation

@jinjunnn

@jinjunnn jinjunnn commented Aug 11, 2026 •

Copy link
Copy Markdown
Owner

Refs #858

不带 Fixes,主 session 裁决。 本票 AC 里的「packaged 窄矩阵 P95 ≤2s + 证据文档」要真打包+签名+
安静真机窗口,与 #881 卡在同一个采样窗口上,本 PR 未验证。合并只闭合代码半场,
票保持 OPEN,由采样窗口跑完后判关 —— 与 #868/#869 当初刻意不带关闭关键字同一处置。

Refs #529, #528

大白话

换 token 的时候,桌面要把旧引擎停掉再用新 token 起一个。停旧引擎时,主进程只发一句「停」,
没告诉引擎为什么停。引擎因此永远走"等现有请求自己结束"那条路;有请求挂着就永远等不完,
主进程干等到预算到期再一刀砍掉它(SIGTERM)。3s 续期档实测因此有约 6.6s 空档,catalog 约 10s。

#869 把这个等待从 6s 收到 500ms,延迟数字好看了,但机制是反的:每一次定时续期都以
"等 500ms → 强杀"收场,引擎自己的收尾流程每次都被砍在半路。

这个 PR 把"为什么停"送过进程边界:token-only 换血时明确要求引擎主动关掉活动连接,
引擎放开连接后自己干净退出,那 500ms 退回成"引擎连强关都做不完"时的安全网。
结构性重启和退出应用照旧等干净,一个字节没动。

改了什么

  • 新增 packages/ui-mac/src/main/sidecar-stop.ts —— main↔sidecar 那条 stop 命令的唯一形状,
    两侧共用(buildSidecarStopCommand / parseSidecarStopCommand / stopSidecarListener)。
  • server.ts:child.postMessage(buildSidecarStopCommand(mode)),token-rotation 带
    closeActiveConnections: true。
  • sidecar.ts:停止命令带该字段时调 listener.stop(true) —— 上游 packages/opencode/src/server/server.ts:186-197
    的 close 分支跑 forceClose = http.closeAll + websockets.closeAll。此前恒 listener?.stop()。
  • 线上形状向后兼容旧形状:{ type: "stop" } 就是"排空"这个合法取值,fail-closed ——
    强关只认显式 true,"1" / 1 / false 一律回落排空。

零改动:index.ts(与 #859 lane 冲突面)、SIDECAR_STOP_TIMEOUT(6s)、
SIDECAR_TOKEN_ROTATION_STOP_TIMEOUT(500ms)、server.test.ts、sidecar-lifecycle.test.ts。

判据为什么不是"停止耗时 < X ms"

"等满预算再 SIGTERM"与"主动关连接后自己退出"落在同一个耗时区间,所以耗时断言对这张票
要修的那件事给出同一个答案 —— 它测不出目标,还被机器快慢左右。

sidecar-stop.test.ts(5 条,已登记进 scripts/gate-files.tsv)断的是行为:让生产
spawnLocalServer 真发那条命令,再让假子进程用生产的 parse + stop executor 消费它,断言
①线上命令带强关指令 ②引擎 listener 真收到 stop(true) ③一次 kill() 都没有。
③ 把"兜底 timer 到期"和"修好了"区分开。

三条反向:结构/退出仍排空(stop() 不传参)、两个兜底预算各自不变、强关只认显式 true。
末条是接线锚 —— sidecar.ts 顶层 registerHooks/getParentPort() 使其结构上无法被 import
(与 #607 同因),这一跳只能锚源码,行为判据在前四条。

绕过实验(每条断言各被证伪一次)

实验前 git status 干净;每次实验后 git checkout -- 还原并复核工作树干净。

变异 结果
B1 还原生产接线:线上 stop 不带 closeActiveConnections(= 修复前) 4 pass / 1 fail — 正向条红,用时 509.09ms(即落回 500ms 兜底 + SIGTERM)
B2 错误实现:所有停止一律强关 4 pass / 1 fail — "结构/退出仍排空"红
B3 错误实现:SIDECAR_STOP_TIMEOUT 也改成 500 4 pass / 1 fail — "两个兜底预算各自不变"红
B4 还原 sidecar 侧:退回 listener?.stop() 4 pass / 1 fail — 接线锚红

本地门(真实输出)

  • bash scripts/alpha-check.sh — 7/7 绿,99 个登记闸门文件全部执行且条数精确一致
    (含新登记的 src/main/sidecar-stop.test.ts 5/5);docs gate 检了 1 个 Markdown。
  • bun run typecheck(packages/ui-mac)— exit 0。
  • bun test src(packages/ui-mac)— 3883 pass / 0 fail / 273 files。
    base(origin/alpha 同 worktree)= 3878 pass / 0 fail / 272 files。
    差 = +5 条 / +1 文件,全部是本 PR 新增;fail-set 差为空。

未做(明确不在本票)

  • 打包窄矩阵 P95 ≤2s 与证据文档未做 —— 需真打包 + 签名 + 安静真机窗口,与 [REQ-161][DECIDE] 归因打包首启 sidecar-ready 之后的目录就绪成本 #881 卡在同一个
    采样窗口上,归验证步。故本 PR 对 packaged 延迟不作任何论断,#858 的其余 AC 由那一步判。
  • 未碰 packages/desktop/src/main/server.ts(上游 opencode 的原壳,不是打包实际走的那条)。

#858 的缺口不在某一行逻辑,在协议少一个字段:「这次只是 token-only 换血」只有 main 知道,
而「要不要主动关掉活动连接」的开关只有 sidecar 按得到(引擎 listener 的 stop(close))。
线上的 { type: "stop" } 一个字段都不带 ⇒ sidecar 恒走排空,旧 sidecar 在有活动连接时把
main 的整个停止预算吃满,新 token 的 sidecar 干等,到期再被 SIGTERM。

把停止原因送过进程边界:token-only 换血的 stop 命令带 closeActiveConnections,sidecar 据此
调 listener.stop(true)(= http.closeAll + websockets.closeAll),关完自己干净退出。
main 侧那个 500ms 退回成兜底安全网 —— 正常路径远早于它。

只收紧 token-only 这一种停止原因:结构性 respawn 与应用退出仍走排空(stop() 不传参,与本票
之前逐字节相同),两个兜底预算 500ms/6s 各自不变。解析 fail-closed:强关只认显式 true。

判据不断耗时 —— 「等满预算再 SIGTERM」与「主动关连接后自己退出」耗时区间相同,那条断言
测不出这张票要修的东西。断的是行为:生产 spawnLocalServer 真发的那条命令带强关指令、
sidecar 侧真把它翻成 listener.stop(true)、且一次 kill 都没有。

不带关闭关键字:#858 的打包窄矩阵(refresh 3s 五样本 P95 ≤2s)未重跑,票保持 open。

Refs #529, #528
@jinjunnn
jinjunnn merged commit 07f5af2 into alpha Aug 11, 2026
6 checks passed
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