@mzzsfy/dsh-bug-perf 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,199 @@
1
+ # dsh-bug-perf 兼容性测试
2
+
3
+ 包定位:宿主缺陷的浏览器半区兜底。四个补丁各自独立开关、独立失败;任何结构不匹配只记录原因,不阻断页面。仅停滞兜底有自绘提示条,其余无 UI;断言走配置路由 + 页面内 `window.__dshBugPerf` 报告 + 页面行为观察。
4
+
5
+ ## 本轮状态(2026-11-08,新增四功能)
6
+
7
+ | 功能 | L1 | L2 装载 | L3 功能面 |
8
+ |------|----|---------|-----------|
9
+ | `restartCoalesce` / `terminalWatchdog` | 通过 | 通过(2026-11-07 隔离工位,见下文本轮实测记录) | 通过(2026-11-07,下述记录仅覆盖这两个功能) |
10
+ | `socketKeepAlive` | 通过(13 用例) | 通过(0.2.2-rc.1 隔离工位,`--only dsh-bug-perf`,端口 9306) | 部分:已确认 `installed: true`、`window.WebSocket.name === 'KeepAliveSocket'`、空闲 5.2 秒发 3 帧且保持 OPEN、持续上行时 0 保活帧;未执行 #15(反代丢 pong)与 #16/#22 |
11
+ | `stuckSession` | 通过(18 用例) | 通过(同上工位) | 通过(隔离工位):停滞 8 秒后 `session stalled, requesting reconnect #1`,宿主侧 `connection lost, retry #1` 真实重建,`performance.timeOrigin` 不变(未刷新页面),用尽 5 次后出现提示条,恢复后消失 |
12
+ | `jobStreamLimit` | 通过(15 用例) | 通过(同上工位) | 通过(生产页面实测):`job/list` 361 → 22,`activeStreams` 368 → 29;3466 个会话里 2711 个子 agent 会话被识别且不占默认窗口,328 次子会话订阅被拒;最长长任务 565 ms → 179 ms,`blockedMs` 1832 ms → 未触发 |
13
+
14
+ - 隔离工位为临时目录,未入库;生产宿主已加载本插件,`window.__dshBugPerf` 在场。
15
+ - 下文的 2026-11-07 实测记录只覆盖 `restartCoalesce` / `terminalWatchdog`,不代表新增功能。
16
+
17
+ ## 测试分层与执行流程
18
+
19
+ | 层 | 内容 | 手段 |
20
+ |----|------|------|
21
+ | L1 单元层 | test/ 七个文件 88 用例(配置归一化 10、重启合并 11、看门狗判定与安装 11、host 路由 10、会话流保活 13、加载停滞兜底 18、任务流上限 15) | 包目录 `node --test "test/*.test.mjs"` |
22
+ | L2 装载层 | 仓库 smoke 含本包;隔离环境 boot 无装载错误,console 出现 `[dsh-bug-perf] applied` | 仓库根 `node scripts/smoke-load.mjs` + 隔离 boot |
23
+ | L3 功能面层 | 下节断言:配置路由、默认安装、独立关闭、冻结自愈、合并计数 | 隔离环境 + playwright 页面探针 |
24
+ | L4 共存层 | 与全家桶共启,页面无 `Failed to load plugins`,四个补丁仍在报告 installed(新增两功能在本轮只完成单包工位装载,见文首状态表) | 隔离环境全 bundle boot |
25
+
26
+ 执行顺序:L1 → L2 → L3 → L4,前层失败即止。
27
+
28
+ ### L1 清单
29
+
30
+ | 文件 | 用例数 | 覆盖点 |
31
+ |------|--------|--------|
32
+ | test/config.test.mjs | 10 | 缺省默认全开;部分覆盖;功能关闭保留阈值;非法类型回退默认;越界收敛;未知字段丢弃;保活与停滞默认值;新增补丁非法值回退;新增补丁越界收敛;任务流上限开关与回退 |
33
+ | test/client-restart.test.mjs | 11 | 窗口内连发尾随一次;窗口为零时逐次执行(关闭合并);窗口外逐次;实例隔离;重复安装幂等;结构缺失跳过;释放后触发不抛;客户端内联配置与宿主同源;缺失远程配置默认值;缺模块记录原因;经模块表安装 |
34
+ | test/client-watchdog.test.mjs | 11 | 未武装不触发;已武装停滞触发;持续新帧不触发;冷却期不重复;页面隐藏不触发;待渲染/无流不触发;未连接不触发;新建视图自动重连;既有视图进入监控;结构缺失跳过;重复安装幂等 |
35
+ | test/client-keepalive.test.mjs | 13 | 空闲发合法 item 帧;按间隔持续发送且 streamId 互不相同;有上行活动不补帧;间隔重配对既有连接生效;非 OPEN 不发送;非会话流连接不发送;关闭后不包装;连接关闭清理定时器;普通断线不熔断;未发保活帧的协议关闭不计数;协议关闭连续命中熔断并恢复原构造器;健康存活清零累计;包装后保持实例与静态常量语义 |
36
+ | test/client-stuck.test.mjs | 18 | 未到阈值不重连;超阈值请求重连;主线程阻塞同样触发重连;重连间隔内不重复;用尽次数后显示提示;恢复后重置并隐藏;无 connection 服务直接提示;重连抛错退化为提示;关闭重连只提示;正文含文案且有消息节点不触发;仅分组行仍判定停滞;隐藏行或空行仍判定停滞;隐藏祖先内的行仍判定停滞;无会话视图不触发;重复安装幂等;重复轮询提示幂等;样式注入带插件标记;样式被删后重装重新注入 |
37
+ | test/client-jobs.test.mjs | 15 | 主会话窗口内放行;超窗口拒绝;子会话不占窗口;点击子会话放行;点击主会话放行;到上限淘汰最老;点击已在窗口不重复建流;点击非会话行忽略;释放后额度回收;重复释放只扣一次;重复安装幂等;服务缺失跳过;摘要缺失按主会话处理;重复订阅只建一次;卸载释放全部流 |
38
+ | test/host.test.mjs | 10 | 注册配置与采样两条路由;缺省返回默认;部分覆盖返回归一化;volatile 解箱;无 webServer 干净跳过;注册失败不抛;注入声明等待 webServer;Config 默认值与共享默认一致;采样上报接受合法载荷;采样上报拒绝坏载荷 |
39
+
40
+ ### L2 判据
41
+
42
+ - 仓库根 `node scripts/smoke-load.mjs`:本包有 `OK @mzzsfy/dsh-bug-perf` 行,全仓 0 FAIL。
43
+ - 隔离环境 boot:bundles = 三基座 + `@mzzsfy/dsh-bug-perf`;boot 日志无本包行级 failed to import / did not activate;页面 console 出现 `[dsh-bug-perf] applied` 且无 `Failed to load plugins`。
44
+
45
+ 本轮实测(2026-11-07,0.2.2-rc.1 隔离工位,22 bundles 全家桶):`node scripts/smoke-load.mjs` 全 OK(含本包);`node scripts/compat/run.mjs --version 0.2.2-rc.1 --port 9303 --seed-gateway` 结果 `activation live=true findings=0`、`browser ok=true rendered=true hasFailBanner=false`,判定 PASS。profile bundles 22 个含 `@mzzsfy/dsh-bug-perf`(依赖为 `link:` 指向仓库包)。页面仅 3 条 `/login` 404(宿主登录态探测,与本包无关)。
46
+
47
+ 0.2.1-rc.2 槽位实测(2026-11-07,`--only dsh-bug-perf --seed-gateway`):`activation live=true findings=0`、`browser ok=true consoleErrors=[]`、`importFailures=[]`、LLM 双协议往返 true;脚本判定 FAIL 的唯一原因是 `anthropicUpstream.userId` 为 `null`(judgeLlm 要求 `dsh:<40hex>` 的 gateway 标记断言,属 0.2.1-rc.2 宿主注释行为,与本包无关)。同工位功能探针:两功能 installed、20 次 `restart()`→3 次 attach、冻结 `revision` 26→27 自愈 + `terminal stalled` 日志 + `echo after-heal` 回显。据此 `dsh.hostMin` 定为 `0.2.1-rc.2`,`dshReleases."0.2.1-rc.2"` 记 `compatible`。
48
+
49
+ ## L3 功能面断言
50
+
51
+ 约定:`<port>` = 隔离环境端口,`<T>` = boot 日志 `token=` 之后的值。
52
+
53
+ ### 一、配置路由面
54
+
55
+ 说明:宿主的 `/api` 前缀有 cookie 会话闸门,直接 curl 无 cookie 返回 401(宿主认证行为,与本包无关)。先拿 boot token 换 cookie,或直接在页面 console 里 `fetch('/api/dsh-bug-perf/config').then(r => r.json())`。
56
+
57
+ | # | 操作 | 预期 |
58
+ |---|------|------|
59
+ | 1 | `curl.exe -s -c cookie.txt -o NUL "http://127.0.0.1:<port>/?token=<T>"` 换取 cookie,再 `curl.exe -s -b cookie.txt "http://127.0.0.1:<port>/api/dsh-bug-perf/config"` | 200,`content-type: application/json`,`config` 四节(restartCoalesce / terminalWatchdog / socketKeepAlive / stuckSession)与 README 默认值逐字段一致 |
60
+ | 2 | 隔离 profile 行加 `config: {terminalWatchdog: {enabled: false}}` 后等热重载(约 1s),再请求 #1 | `terminalWatchdog.enabled` 为 `false`,`restartCoalesce` 保持默认 |
61
+
62
+ ### 二、默认安装面
63
+
64
+ | # | 操作 | 预期 |
65
+ |---|------|------|
66
+ | 3 | 打开页面,console 执行 `window.__dshBugPerf` | `features.restartCoalesce.installed === true`;`features.terminalWatchdog.installed === true`(打开过一次终端后;未打开终端时允许为 `waiting for webTerminals` 后转 installed) |
67
+ | 4 | console 执行 `window.__dshBugPerf.features.restartCoalesce`;合并是否真实发生看 #8 的 open 帧计数 | `installed: true`;实际合并效果以 #8 为准 |
68
+
69
+ ### 三、独立开关面
70
+
71
+ | # | 操作 | 预期 |
72
+ |---|------|------|
73
+ | 5 | 行配置 `restartCoalesce: {enabled: false}` 后刷新页面 | `features.restartCoalesce.reason === 'disabled'`;`RemoteStream.prototype.restart` 保持官方原名(未包装) |
74
+ | 6 | 行配置 `terminalWatchdog: {enabled: false}` 后刷新页面,打开终端 | `features.terminalWatchdog.reason === 'disabled'`;`webTerminals.view` 未被包装(冻结场景不会自愈) |
75
+ | 7 | 四个功能都关闭后刷新页面 | 页面正常,console 无错误,`window.__dshBugPerf.features` 四节均为 disabled |
76
+
77
+ ### 四、重启合并面
78
+
79
+ | # | 操作 | 预期 |
80
+ |---|------|------|
81
+ | 8 | 打开终端,在页面内对同一 stream 连打 20 次 `restart()`,每次间隔 5ms,记录 WebSocket `terminal/attach` open 帧 | 首次立即 + 窗口内一次尾随(实测约 2-3 次 open),而非 20 次 |
82
+ | 9 | 间隔 150ms 打 3 次 | 3 次均实际重开(窗口外不合并) |
83
+
84
+ ### 五、终端自愈面
85
+
86
+ | # | 操作 | 预期 |
87
+ |---|------|------|
88
+ | 10 | 打开终端并点「接管输入」,执行 `echo ready` 确认可写;冻结构造无稳定手段(宿主的 `view.connect()` 会先 detach 再新建流,不能制造并发 attach),改为在真实故障窗口(宿主重建 generation、输出静默)观察;再输入 `echo frozen` | 无回显,`view.revision` 停滞(`view.state.getSnapshot().writable` 为 false,冻结指纹)。P:依赖真实故障,非按需可复现 |
89
+ | 11 | 继续等待 14 秒 | console 出现 `[dsh-bug-perf] terminal stalled, reconnecting`;之后输入 `echo after-heal` 回显,`writable` 恢复 true |
90
+ | 12 | 只读旁观视图(从未点接管输入)静置 30 秒 | 不触发自愈,不产生新的 attach open 帧 |
91
+
92
+ ### 六、会话流保活面
93
+
94
+ | # | 操作 | 预期 |
95
+ |---|------|------|
96
+ | 13 | 页面加载后 console 执行 `window.__dshBugPerf.features.socketKeepAlive` 与 `window.WebSocket.name` | `installed: true`;构造器名为 `KeepAliveSocket` |
97
+ | 14 | DevTools Network → WS → `/api/remote.mux` → Messages,空闲观察 30 秒 | 约每 `intervalMs` 一条 `{"type":"item","streamId":"dsh-bug-perf-keepalive-...","value":0}`(默认间隔下约 20 条);连接保持 OPEN,无 close |
98
+ | 15 | 让反代丢弃 pong 但转发客户端帧(或抓包确认真实链路只丢 pong),保持 60 秒 | 连接不因心跳超时被 terminate;链路整体中断时仍会断开并走宿主重连(保活不能挽救真断网) |
99
+ | 16 | 行配置 `socketKeepAlive: {enabled: false}` 后刷新页面 | `features.socketKeepAlive.reason === 'disabled'`;`window.WebSocket` 为原生构造器;Messages 面板无保活帧 |
100
+ | 22 | 行配置 `socketKeepAlive: {intervalMs: 5000}` 后刷新页面,观察 20 秒 | 保活帧间隔约 5 秒(既有连接即按新间隔,无需等重连) |
101
+
102
+ ### 七、加载停滞兜底面
103
+
104
+ | # | 操作 | 预期 |
105
+ |---|------|------|
106
+ | 17 | 在 console 构造加载态:清空 `[data-chat-flow]` 的子节点后追加一个直接子节点,其文本为「载入历史…」,确保容器内没有有效消息行(无 `data-chat-flow-key`,或只有分组/隐藏/空行) | 超过 `stallMs` 后 console 出现 `[dsh-bug-perf] session stalled, requesting reconnect #1`,`connection.state` 转为 connecting→connected,页面不刷新(`performance.timeOrigin` 不变),会话流恢复正常 |
107
+ | 18 | 恢复会话(加载文案消失、消息回来) | 提示条(若有)被移除,重连计数清零 |
108
+ | 19 | 保持停滞,让重建次数用尽(默认 5 次、每次间隔 15s) | 停止请求重连,出现 `#dsh-bug-perf-stuck` 提示条与「刷新页面」按钮;不再自动刷新页面 |
109
+ | 20 | 行配置 `stuckSession: {reconnect: false}` 后停滞 | 不请求重连,直接显示提示条 |
110
+ | 21 | 行配置 `stuckSession: {maxReconnectAttempts: 1}` 后停滞 | 只请求一次重连,随后显示提示条 |
111
+ | 23 | 正常会话里有用户消息文本含「载入历史…」,静置 2 分钟 | 不触发重连、不插入提示条(负向用例) |
112
+ | 24 | 打开会话很多的工位,在 console 读 `window.__dshBugPerf.diag.sockets` 的 `streams` 分布与 `features.jobStreamLimit.state` | `job/list` 条目数等于主会话数且不超过 `base`;`rejectedSubagent` 大于 0,`subagentSessions` 与 `summaries` 反映真实分布(实测 2711/3466) |
113
+ | 25 | 关闭 `jobStreamLimit` 后刷新 | `job/list` 条目数回到会话行数量级(实测 361) |
114
+ | 26 | 点击一个未被订阅的会话 | 该会话出现 `job/list` 流,`state.clicked` 增加;总数超过 `max` 时 `state.evicted` 增加且总数不变 |
115
+ | 27 | 折叠/展开侧边栏或切换工作区 | `job/list` 流保持稳定,不出现重复流(`watchRows` 同一会话只建一条) |
116
+
117
+ ## L4 共存层
118
+
119
+ - 全家桶 bundles 共启:页面无 `Failed to load plugins`,四个补丁 report 仍 installed,终端与流式回复正常;保活帧不引发额外重连。
120
+ - 与 `@mzzsfy/dsh-shared-terminal` 共存:shared-terminal 的只读自愈与本包看门狗互不干扰(前者点「接管输入」,后者调 `view.connect()`),同一终端不出现重复重连风暴。
121
+
122
+ ### 本轮实测记录(2026-11-07,0.2.2-rc.1,22 bundles)
123
+
124
+ | 断言 | 实测 |
125
+ |------|------|
126
+ | 页面加载 | `window.__dshBugPerf.features` 两节均 `installed: true`(看门狗在 `webTerminals` 就绪后转为 installed,日志 `[dsh-bug-perf] terminal watchdog installed`) |
127
+ | 共存 | `document.querySelector('style[data-plugin="@mzzsfy/dsh-shared-terminal"]')` 在场;无 `Failed to load plugins`;本次终端始终可写(未进只读态,页面无「接管输入」按钮),shared-terminal 的只读自愈未被触发,看门狗独立完成 `view.connect()` 恢复 |
128
+ | 合并(#8) | 同一 stream 连打 20 次 `restart()`(5ms 间隔)→ WebSocket `terminal/attach` open 帧 3 次 |
129
+ | 独立开关(#5) | profile 行加 `config: {restartCoalesce: {enabled: false}}` 热重载后,report 为 `{installed:false, reason:'disabled'}`;同样 20 次 `restart()` → 20 次 open;`terminalWatchdog` 仍 `installed:true` |
130
+ | 冻结自愈(#10/#11) | 注入 10 个并发 attach 流后 `revision` 停在 22、`writable:false`、`echo frozen` 无回显;时间线每秒采样:第 0-6 秒仍冻结,第 7 秒恢复(`revision:23`、`writable:true`)并出现 `[dsh-bug-perf] terminal stalled, reconnecting` 日志;随后 `echo after-heal` 回显成功 |
131
+ | 配置路由(#1/#2) | 带会话 cookie 的 `GET /api/dsh-bug-perf/config` 返回默认配置;热改 `enabled:false` 后返回 `false` |
132
+
133
+ 取证文件(本地工位,不入库):`.compat/disc-probe/family1.json`、`family1-frozen.png`、`family1-healed.png`、`timeline1.json`(冻结-恢复逐秒时间线)、`family-off.json`(均为 2026-11-07 旧两功能记录)。`.compat/0.2.2-rc.1/` 只有 2026-11-08 单包 `--only dsh-bug-perf` 工位的 `page.json`/`l3-llm.json`,不是 22 bundles 全家桶结果。
134
+
135
+ ### 真实浏览器复核(dev-browser 有头 Chrome,token 隔离)
136
+
137
+ 同一隔离工位(端口 9304,22 bundles)用真实有头浏览器重跑,规避无头浏览器的渲染/输入差异:
138
+
139
+ | 断言 | 实测 |
140
+ |------|------|
141
+ | 安装报告 | `window.__dshBugPerf` 两功能 `installed: true`,配置为默认值 |
142
+ | 合并(#8) | 终端内同一 stream 连打 20 次 `restart()`(5ms 间隔)→ WebSocket `terminal/attach` open 帧 3 次 |
143
+ | 冻结自愈(#10/#11) | 注入 10 个并发 attach 流后逐秒采样:`revision` 停在 28 共 6 秒(第 0-5 秒 `writable:false`),第 6 秒恢复 `revision:29`/`writable:true`;第二轮冻结捕获到 `[dsh-bug-perf] terminal stalled, reconnecting {sessionId, id}`;键盘输入 `echo healed2` 回显成功 |
144
+ | 独立开关(#5) | 行配置 `restartCoalesce.enabled:false` 热重载后刷新页面:report 为 `{installed:false,reason:'disabled'}`,`m.stream.restart` 为官方原函数(`restart() { if (this.lifetime.signal... }`),20 次 `restart()` → 20 次 open;`terminalWatchdog` 仍 installed |
145
+ | 恢复默认 | 删配置热重载后刷新:report 回到两功能 installed,`config.restartCoalesce.enabled:true` |
146
+
147
+ 取证:`.compat/disc-probe/browser-frozen.png`、`browser-enabled.png`(有头浏览器截图);输入用 textarea 派发 `keypress` + `Enter`(keyCode 13),`type` 逐字符对 xterm 会重复字符,不计入产品行为。
148
+
149
+ ## 兼容性维度适用表
150
+
151
+ 标记:M 手工必测 / A 自动化覆盖 / C 条件必测 / N 不适用。维度定义与判定口径见 [兼容性检查总纲](../../docs/兼容性测试/兼容性检查总纲.md)。
152
+
153
+ | 维度 | 标记 | 本包落点(检查点/证据) |
154
+ |---|---|---|
155
+ | D01 宿主代际与 API 契约 | M | 依赖宿主浏览器内部形态:`RemoteStream.prototype.restart`、`webTerminals` 视图、client 模块表、`/api/remote.mux` 路径与 `item` 帧协议(未知 streamId 被忽略)、`[data-chat-flow]` 容器与加载提示文案、全局 `WebSocket` 可包装;`hostMin=0.2.1-rc.2`,但 `socketKeepAlive`/`stuckSession` 目前只在 0.2.2-rc.1 验证;`installed: true` 只表示包装装上,不等于对既有连接生效;结构不匹配只记录原因或静默失效 |
156
+ | D02 运行平台与架构 | N | 纯浏览器半区补丁,无平台分支、文件、子进程与端口;host 半区只做配置分发 |
157
+ | D03 Web 客户端形态 | M | Chromium 真机;终端 xterm 视图与 attach 流;页面隐藏/可见、待渲染/无流/未连接分支;窄屏与刷新 |
158
+ | D04 宿主部署与配置形态 | C | 触发条件 = 行配置变更。落点:独立开关经行 config 分发,宿主路由热重载约 1s 生效;有宿主按 `Config` 自动生成的插件详情配置表单,无自绘 UI;客户端开关在页面加载时读取一次,修改后刷新页面生效(与宿主路由热重载不同) |
159
+ | D05 插件互操作与组合 | M | 与官方模块/其他终端补丁(shared-terminal 只读自愈)共存;同一终端不出现重复重连风暴;全家桶无 `Failed to load plugins`;新功能与惰性保活帧不影响其它补丁(共享 WS 只多出 item 帧) |
160
+ | D06 生命周期 | M | 安装幂等;关闭开关恢复官方原方法;刷新页面后包装撤销,运行中禁用不撤销(已知行为);HMR 后不叠加包装;释放后迟到触发不抛 |
161
+ | D07 多实例与并发 | C | 触发条件 = 多窗口/多终端。落点:实例隔离、重复安装幂等、同一 stream 并发 restart 合并、每视图独立看门狗 |
162
+ | D08 持久化代际与迁移 | N | 无自有持久化;配置在宿主 profile 行,由宿主管理 |
163
+ | D09 数据规模与异常数据 | N | 无用户数据面;只处理流与终端事件,不落库 |
164
+ | D10 配置读写与默认值 | M | 缺省默认全开;部分覆盖;功能关闭保留阈值;非法类型回退默认;越界收敛;未知字段丢弃;volatile 解箱;四节字段齐全 |
165
+ | D11 降级与错误路径 | M | 模块/视图缺失跳过并记录原因,不影响页面;DOM/协议形态缺失(会话流路径、加载提示结构)时静默失效,由 D01 复核发现;未武装/未连接/页面隐藏不触发;重连失败有冷却;熔断后恢复原始构造器并把原因写回 report |
166
+ | D12 安全边界与信任 | C | 触发条件 = 路由对外可达。落点:只读 GET `/api/dsh-bug-perf/config`(exact 路由),无写面/密钥;exact 路由不经 connection 的 `/api` 前缀闸门,认证由宿主 startup-auth 承担(本窗口未验证);不设自有认证层 |
167
+ | D13 性能与资源 | M | 包装调用开销、合并窗口(默认 100ms)、看门狗冷却与重连频率;保活每条被覆盖连接一条 500ms 检查节拍、默认约 40 帧/分钟;停滞检查每 2s 读一次有效消息行;主动重建默认最多 5 次、间隔 15s;不得形成重连风暴与请求风暴;关闭后零包装开销 |
168
+ | D14 呈现与可访问性 | C | 触发条件 = 停滞提示条出现。落点:固定顶部提示条 + 「刷新页面」按钮,`role="status"`,文案固定中文(插件无 i18n 设施),校验亮/暗主题、窄屏、键盘可达与对比度 |
169
+ | D15 观测与判定面 | M | `window.__dshBugPerf.features` report(installed/reason)、`[dsh-bug-perf] applied`/stalled 日志、配置路由回读、console 0(`/login` 404 属宿主噪声,按总纲 G8 豁免) |
170
+
171
+ ## 维度检查点(补充)
172
+
173
+ 1. **官方形态复核**:宿主升级后逐项核对 `RemoteStream.restart`、`webTerminals.view`、client 模块表、`/api/remote.mux` 路径与 `item` 帧协议、`[data-chat-flow]`/`[data-chat-flow-key]` 结构、加载提示文案、全局 `WebSocket` 可包装性;模块/视图缺失时该补丁记 `waiting for ...` 或 `reason`,页面不报错;DOM/协议形态缺失时静默失效,须按 L3 #13-#23 复核。
174
+ 2. **开关往返**:关闭 → 官方原名/未包装 → 重开 → 包装恢复;刷新页面后不残留包装,运行中禁用不撤销(已知行为)。
175
+ 3. **合并边界**:窗口内 20 次 restart → 首次立即 + 一次尾随(约 2-3 次 open);窗口外逐次;不同 stream 不互相合并。
176
+ 4. **看门狗边界**:未武装、页面隐藏、待渲染、无流、未连接均不触发;持续新帧不触发;冷却期不重复;新建视图自动武装。
177
+ 5. **共存**:与 shared-terminal 同装时只读自愈走「接管输入」、看门狗走 `view.connect()`,两条路径不得同时抢同一终端。
178
+ 6. **已知盲区**:本包是官方修复前的兜底,不是兼容性承诺;宿主已修复时应移除补丁而不是继续叠加。
179
+
180
+ ## 实测记录(维度面)
181
+
182
+ 2026-11-07,0.2.2-rc.1 隔离工位:配置路由、默认安装、独立开关、合并计数、冻结自愈与真实有头浏览器复核均已执行(见上两节);D07 多窗口并发、D13 关闭后开销未单独取证,执行后在此登记。
183
+
184
+ ## 本包验证点(最小判定)
185
+
186
+ | 维度 | 验证点(操作→预期) | 证据 | 级别 |
187
+ |---|---|---|---|
188
+ | D01 | 隔离工位冷启动后,等 `window.__dshBugPerf.config` 出现(配置异步到达)再读 `features` → `restartCoalesce`/`socketKeepAlive`/`stuckSession` 均 `installed: true`,`window.WebSocket.name` 为 `KeepAliveSocket`;打开终端后 `terminalWatchdog` 转为 `installed: true`(冷启动先 `waiting for webTerminals`);模块/视图缺失时该节转 `waiting for ...` 或 `reason`,页面不报错;`/api/remote.mux` 或加载提示结构缺失时保活/停滞兜底静默失效(`installed` 仍为 true),须在宿主升级后按 L3 #13-#23 复核 | `window.__dshBugPerf` JSON 快照;console 0 错误 | B |
189
+ | D03 | Chromium 真机开终端,切页面隐藏/可见、缩窄视口、刷新后重开 xterm 视图 → 终端可写、attach 流正常;页面隐藏不触发看门狗;刷新后包装按页面加载重建;无 `Failed to load plugins` | 页面截图;console 日志;features 报告 | B |
190
+ | D04 | 触发 = 行配置变更。行加 `config: {terminalWatchdog: {enabled: false}}` 等宿主路由热重载约 1s 后 `GET /api/dsh-bug-perf/config`,再刷新页面读 report → 路由回读 `terminalWatchdog.enabled === false` 且 `restartCoalesce` 保持默认;客户端开关需刷新页面才生效(与路由热重载不同) | 配置路由响应 JSON;report 前后对照 | C |
191
+ | D05 | 与 `@mzzsfy/dsh-shared-terminal` 及全家桶同载,同一终端点「接管输入」并注入冻结 → 只读自愈与看门狗不抢同一终端,无重复重连风暴;`style[data-plugin="@mzzsfy/dsh-shared-terminal"]` 在场;无 `Failed to load plugins`。P:2026-11-07 工位终端始终可写,只读自愈路径未触发,该分支待补 | console 重连日志条数;页面截图 | B |
192
+ | D06 | 重复加载页面、关开关后刷新、运行中禁用不刷新 → 安装幂等不叠加;关闭后 `RemoteStream.prototype.restart` 为官方原名、`webTerminals.view` 未包装;刷新后包装撤销;运行中禁用不撤销属已知行为 | `m.stream.restart` 函数体首行;report 的 installed/reason | B |
193
+ | D07 | 触发 = 多窗口/多终端。开两个终端与两个窗口,在同一 stream 并发 `restart()`(5ms 间隔 20 次),另一窗口独立操作 → 同 stream 合并为约 2-3 次 open;不同 stream 不互相合并;每视图看门狗独立、重复安装幂等 | WebSocket `terminal/attach` open 帧计数 | C |
194
+ | D10 | 分别写入非法类型、越界、未知字段、部分覆盖的 config 后 `GET /api/dsh-bug-perf/config` → 非法类型回退默认、越界收敛、未知字段丢弃、功能关闭保留阈值、volatile 解箱、缺省默认全开 | 配置路由响应 JSON(四节:restartCoalesce / terminalWatchdog / socketKeepAlive / stuckSession) | B |
195
+ | D11 | 人为移除被包装面(模块表缺 `RemoteStream`)、令 `webTerminals` 未就绪、页面隐藏、重连失败 → 结构缺失跳过并记录原因且页面不报错;未武装/未连接/页面隐藏不触发;重连失败有冷却 | report 的 reason 字符串;console 日志 | B |
196
+ | D12 | 触发 = 路由对外可达。带会话 cookie `GET /api/dsh-bug-perf/config`,再伪造 Host 头直连同一路由 → 只读 GET 返回配置(exact 路由不经 connection 的 `/api` 前缀闸门),无写面与密钥;认证由宿主 startup-auth 承担,伪造 Host 不判缺陷 | 响应 JSON;状态码 | C |
197
+ | D13 | 同一 stream 连打 20 次 `restart()`(5ms 间隔),再以 150ms 间隔打 3 次,关闭功能后重复 20 次 → 窗口内首次立即 + 一次尾随(实测 3 次 open),窗口外 3 次逐次重开;关闭后零包装开销(20 次 → 20 次 open);会话流空闲 30 秒保活帧约 20 条且无额外 HTTP 请求;无重连风暴 | `terminal/attach` open 帧计数;WS Messages 帧计数 | B |
198
+ | D14 | 触发 = 提示条出现。在亮/暗主题与窄屏下触发停滞 → 提示条固定顶部不遮挡输入区,文本与按钮对比可读,按钮可 Tab 聚焦并回车触发刷新,`role="status"`;英文界面下提示文案仍为中文(插件无 i18n,已知限制) | 亮暗截图;键盘操作描述 | C |
199
+ | D15 | 冷启动后查 console、`window.__dshBugPerf`、配置路由回读与 boot.log → 出现 `[dsh-bug-perf] applied`,看门狗就绪后 `[dsh-bug-perf] terminal watchdog installed`,冻结自愈打印 `[dsh-bug-perf] terminal stalled, reconnecting`;console 错误 0(`/login` 404 按总纲 G8 豁免) | result.json 的 `browser ok`/`hasFailBanner` 字段;日志截图;report JSON | B |