@twinsearth/w2m-dsh-plugin 3.2.0 → 3.2.2
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.
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +58 -0
- package/README.md +1 -1
- package/codex/mcp-servers.toml +1 -1
- package/docs/INTEROP-CONTRACT.md +126 -0
- package/docs/acceptance/3.2.1/RELEASE-3210.json +50 -0
- package/docs/acceptance/3.2.2/RELEASE-3220.json +53 -0
- package/lib/client.js +1216 -3
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/src/plugin/tools.mjs +1 -1
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "The w2m fleet plugins: one account, many machines, any of Windows, macOS or Linux.",
|
|
8
|
-
"version": "3.2.
|
|
8
|
+
"version": "3.2.2"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "w2m-fleet",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"description": "Read fleet state and dispatch commands across the machines registered to one account. Read-only MCP surface: the five exposed tools cannot change anything.",
|
|
15
|
-
"version": "3.2.
|
|
15
|
+
"version": "3.2.2",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "TwinsEarth"
|
|
18
18
|
},
|
|
@@ -26,5 +26,5 @@
|
|
|
26
26
|
]
|
|
27
27
|
}
|
|
28
28
|
],
|
|
29
|
-
"version": "3.2.
|
|
29
|
+
"version": "3.2.2"
|
|
30
30
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "w2m-fleet",
|
|
3
3
|
"description": "Drive the same project on online Windows, macOS and Linux machines from one account: read fleet state, dispatch commands and read results. The MCP surface is deliberately read-only.",
|
|
4
|
-
"version": "3.2.
|
|
4
|
+
"version": "3.2.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TwinsEarth"
|
|
7
7
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,64 @@ All notable changes to this project are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [3.2.2] - 2026-11-14
|
|
8
|
+
|
|
9
|
+
- 决策中心对齐瓮城的剩余能力(v3.2.3 之前合并进本版):
|
|
10
|
+
- **每会话「待选」浮钮**:会话席位(`conversation.input.left`)发布当前 sessionId;当该会话有 pending 决策时,
|
|
11
|
+
右侧上方出现「待选 N」浮钮(红票变红),点击打开只看该会话的抽屉(带「显示全部会话」清除筛选);
|
|
12
|
+
- **键盘导航**:抽屉内 `↑/↓` 移动选中项、`1-9` 按选中票的第 N 个选项处理、`r` 刷新、`Esc` 关闭;
|
|
13
|
+
- **按等级批量处理**:每个等级分组头部在 ≥2 张 pending 时出现「批量解决 / 批量放弃」,走 `resolveMany`/`abandonMany`;
|
|
14
|
+
红色/阻塞票按宿主红线拒绝批量(按钮置灰并提示“必须逐条处理”),批量结果如实显示成功数与未处理原因;
|
|
15
|
+
- **分叉聚合 / 生产者面板**:抽屉底部可展开,展示 `/forks` 聚合与 `/producers` 目录;宿主不提供时如实说明;
|
|
16
|
+
- **会话筛选**:抽屉顶部显示“只看会话:xxx”并可一键清除。
|
|
17
|
+
- 右侧 `sidebar.right.pane.tab` 页签仍暂缓:宿主要求先向 `sidebarRightTabs` 注册 tab 类型再挂 slot,
|
|
18
|
+
为避免再次引入客户端稳定性问题,本轮未接线(功能已由浮标/侧栏/抽屉覆盖)。
|
|
19
|
+
- **严重修复(v3.2.1 回归)**:决策收件箱与队列日志的遮罩层/抽屉用了 `hidden` 属性切换,但样式表里
|
|
20
|
+
`.w2mDcOverlay{display:flex}` 是作者规则、`[hidden]{display:none}` 只是 UA 规则,作者规则获胜 ——
|
|
21
|
+
遮罩层永远铺满全屏并吞掉点击,点关闭只改状态不改显示,表现为「整个客户端被弹窗锁死、点哪都没反应、
|
|
22
|
+
点空白也不收回」。现在改为显式 `style.display` 开关 + 全局 `[hidden]{display:none!important}` 兜底,
|
|
23
|
+
并在真实浏览器里断言:打开时抽屉 `display != none`、关闭后抽屉与遮罩 `display == none`、
|
|
24
|
+
页面中心点击不再被遮罩拦截(`elementFromPoint` 不在遮罩内)。
|
|
25
|
+
- 重要提示:遇到 3.2.1 锁屏不需要删除整个 profile,删除该 profile 的
|
|
26
|
+
`node_modules/@twinsearth/w2m-dsh-plugin` 目录(或把 `dsh.profile.bundles` 里的 w2m 行移除)即可恢复;
|
|
27
|
+
本版修好后重新安装即可。
|
|
28
|
+
|
|
29
|
+
- 修复:队列子插件(queue-journal)只有宿主路由与 composer 内联按钮,草稿/队列为空时整块不渲染
|
|
30
|
+
(瓮城原版行为),用户看到的就是「未生效」。本版把队列日志做成常驻入口,并用真实 DSH 页面验证。
|
|
31
|
+
- 右侧中部新增第二枚「队列」浮标(收件箱浮标下方,固定可见,显示保存条数),点开右侧抽屉:
|
|
32
|
+
按时间列出所有会话的草稿/队列快照(会话键、更新时间、草稿预览、排队消息逐条预览),
|
|
33
|
+
每张卡片提供「恢复草稿」(写回输入框)、「恢复队列」(`/api/w2m/composer/queue`,
|
|
34
|
+
无 live 会话时如实报 session-not-live)、「清除」,全部由用户点击触发,插件从不自动发送消息。
|
|
35
|
+
- composer dock 常驻 chip:没有草稿时也显示「队列日志 / 打开」,不再整块消失(原版 return null)。
|
|
36
|
+
- GPW 面板头部新增「队列 N」按钮(与「收件箱」按钮并列),点开同一个队列日志抽屉。
|
|
37
|
+
- 数据面:`GET /api/w2m/composer/journal?list=1` 列表、`POST /api/w2m/composer/journal` 清除、
|
|
38
|
+
`POST /api/w2m/composer/queue` 恢复;抽屉每 8 秒刷新一次,打开时立即刷新。
|
|
39
|
+
- composer dock 的注册 id 从 `fang-queue-journal` 改为 `w2m-queue-journal`,避免与本机可能同时安装的
|
|
40
|
+
瓮城 queue-journal 插件撞 id。
|
|
41
|
+
- 验证:`test/client-bundle.test.mjs` 36/36;`test/loader-entry.test.mjs` 10/10,真实 Chromium 页面探针新增:
|
|
42
|
+
队列路由 POST/list 均 200、浮标存在、点击打开抽屉、抽屉标题含「队列日志」、抽屉正文含刚保存的草稿、
|
|
43
|
+
GPW 面板含队列日志按钮(composer dock 是会话级插槽,落地页可能不渲染,探针以诊断输出而不误判)。
|
|
44
|
+
- 与瓮城对比仍暂缺(后续版本):抽屉键盘导航、批量恢复、按会话筛选、时间线历史快照分组。
|
|
45
|
+
|
|
46
|
+
## [3.2.1] - 2026-11-14
|
|
47
|
+
|
|
48
|
+
- 修复:v3.0.0/v3.2.0 的决策中心只有宿主与路由,没有把瓮城的两个用户入口挂出来,表现为
|
|
49
|
+
「没有右侧浮标&浮窗」「GPW 里没有收件箱按钮」。本版补齐客户端 UI,并用真实 DSH 页面验证。
|
|
50
|
+
- 右侧居中的「收件箱」浮标(固定`right:0; top:50%`,红/黄/无三种色调 + 未读数)+ 从右向左的
|
|
51
|
+
抽屉:分组(红/黄/蓝/绿/灰)、过滤(待处理/全部/已归档 + 等级)、选项按钮、自定义答复、
|
|
52
|
+
AI 建议、放弃、上下文折叠、Escape/遮罩关闭、红色 ticket 顶部提醒(不自动处理)。
|
|
53
|
+
- 数据面:`/api/w2m/decisions*` 的 list + SSE(`/events`)实时推送,断线自动回退 4 秒轮询;
|
|
54
|
+
resolve 支持票面选项与自定义答复,abandon 单票放弃,`ai/advise` 只展示建议不自动执行。
|
|
55
|
+
- GPW:面板头部新增「收件箱 N」按钮(GPW 设置页与 GPW 浮标打开的面板都有),点击打开同一个抽屉;
|
|
56
|
+
原有面板内联摘要保留。
|
|
57
|
+
- 侧栏入口:新增「收件箱」侧栏席位(图标 + 未读红点),对齐瓮城的收件箱侧栏入口。
|
|
58
|
+
- AI 设置:抽屉内新增「AI 建议设置」(provider / model / baseUrl / apiKey + 保存 + 测试连接),
|
|
59
|
+
对应 `/ai/config` 与 `/ai/test`;没有配置时如实提示,而不是让建议按钮静默失败。
|
|
60
|
+
- 验证:`test/client-bundle.test.mjs` 36/36(座位断言更新为 18 个,含收件箱侧栏席位);
|
|
61
|
+
`test/loader-entry.test.mjs` 10/10,新增真实 Chromium 页面探针(`W2M_DECISION_PROBE=1`):
|
|
62
|
+
浮标存在、点击打开抽屉、抽屉标题含「收件箱」、关闭生效、侧栏入口存在、GPW 面板含收件箱按钮。
|
|
63
|
+
- 与瓮城对比仍暂缺(后续版本补):收件箱的键盘导航、按等级批量处理、分叉聚合/生产者面板的完整展示。
|
|
64
|
+
|
|
7
65
|
## [3.2.0] - 2026-11-14
|
|
8
66
|
|
|
9
67
|
- 互联契约子插件:`@twinsearth/w2m-dsh-plugin/contract`,基于独立发布的版本化契约 SDK
|
package/README.md
CHANGED
|
@@ -93,7 +93,7 @@ w2m-mcp --help
|
|
|
93
93
|
# 复制进你自己的 codex/mcp-servers.toml
|
|
94
94
|
[mcp_servers.w2m]
|
|
95
95
|
command = "npx"
|
|
96
|
-
args = ["-y", "@twinsearth/w2m-dsh-plugin@3.2.
|
|
96
|
+
args = ["-y", "@twinsearth/w2m-dsh-plugin@3.2.2", "w2m-mcp"]
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
### 5. 验证过的 release tarball(离线)
|
package/codex/mcp-servers.toml
CHANGED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# w2m ↔ 瓮城 dsh-Fang ↔ 房主 dsh-Master:独立版本化互联契约(contract 1.x)
|
|
2
|
+
|
|
3
|
+
本文是 w2m 仓库内的契约层设计说明。三个项目相互独立:各有 package / 版本 / repo / 发布节奏,
|
|
4
|
+
**互不 import 内部代码**,只通过本文描述的契约 SDK 互联。w2m 的实现文件在 `src/interop/`,
|
|
5
|
+
SDK 是 `@twinsearth/dsh-fang-contract@1.1.1` 的逐字节副本(来源与哈希见
|
|
6
|
+
`src/interop/contract/PROVENANCE.md`)。
|
|
7
|
+
|
|
8
|
+
## 1. 要解决的问题
|
|
9
|
+
|
|
10
|
+
两个主插件(瓮城 dsh-Fang、房主 dsh-Master)各自独立发布。如果它们互相 import 内部代码,
|
|
11
|
+
任何一侧改接口都会把另一侧锁死版本;如果它们各自发明私有 IPC,又会退化成两套协议。
|
|
12
|
+
契约层把「怎么说话」从「说什么」里拆出来:
|
|
13
|
+
|
|
14
|
+
| 关注点 | 归属 |
|
|
15
|
+
| ---------------------- | ------------------------------------------------------------ |
|
|
16
|
+
| 方法名与语义(说什么) | 契约包的方法表(`METHODS` + 本仓库的 `W2M_METHODS`) |
|
|
17
|
+
| 信封格式(怎么打包) | 契约包的 `envelope.js`(`fang-master` 协议 v1) |
|
|
18
|
+
| 版本协商(能不能听) | `contract.hello` + `negotiate()`(MAJOR 兼容、MINOR 取交集) |
|
|
19
|
+
| 传输(走哪条路) | `transports.js`:local → http → file → legacy 四级降级 |
|
|
20
|
+
|
|
21
|
+
## 2. 版本与兼容承诺
|
|
22
|
+
|
|
23
|
+
| 变更类型 | 版本位 | 规则 |
|
|
24
|
+
| ---------------------------- | ------ | --------------------------------------------------------------------------------------- |
|
|
25
|
+
| 新增方法/能力/字段 | MINOR | 旧客户端忽略未知字段;新方法不在旧端方法集内就自动降级 |
|
|
26
|
+
| 修 bug、无行为变化 | PATCH | 两侧可独立升级 |
|
|
27
|
+
| 删除方法、改语义、改信封结构 | MAJOR | 保留上一 MAJOR 方法 ≥ 1 个 MINOR 周期(双写 `method@old` / `method@new`),提供迁移说明 |
|
|
28
|
+
|
|
29
|
+
`hello` 返回 `protocol / contractVersion / role / instanceId / methods / capabilities / transports`;
|
|
30
|
+
`negotiate()` 输出 `{compatible, version, methods, capabilities, fallback}`:
|
|
31
|
+
|
|
32
|
+
- 对端 MAJOR 更高 → `remote-too-new` → 自动降级到 legacy 传输;
|
|
33
|
+
- 对端 MAJOR 更低 → `remote-too-old` → 自动降级;
|
|
34
|
+
- 同 MAJOR → 取双方方法交集 + 公共 minor 裁剪。
|
|
35
|
+
|
|
36
|
+
三个版本号互不绑定:
|
|
37
|
+
|
|
38
|
+
| 版本 | 当前值 | 管什么 |
|
|
39
|
+
| ------------- | ------- | ------------------------------- |
|
|
40
|
+
| 插件版本 | `3.2.0` | w2m 自身的 npm/tag/Release |
|
|
41
|
+
| w2m 契约版本 | `1.0.0` | `w2m.*` 方法表的语义 |
|
|
42
|
+
| 契约 SDK 版本 | `1.1.1` | 信封、协商、传输、`fang.*` 方法 |
|
|
43
|
+
|
|
44
|
+
## 3. 方法表
|
|
45
|
+
|
|
46
|
+
协议级(SDK 提供):
|
|
47
|
+
|
|
48
|
+
| 方法 | 用途 | 契约版本 |
|
|
49
|
+
| ---------------------------------------------------- | ------------------- | --------- |
|
|
50
|
+
| `contract.hello` | 版本/角色/能力协商 | 1.0 |
|
|
51
|
+
| `contract.health` | 健康检查 | 1.0 |
|
|
52
|
+
| `fang.*`(fork/decision/session/orchestrator/relay) | 瓮城 ↔ 房主既有能力 | 1.0 / 1.1 |
|
|
53
|
+
|
|
54
|
+
w2m 自己的命名空间(本仓库 `src/interop/index.mjs`,不占用也不改 `fang.*` 语义):
|
|
55
|
+
|
|
56
|
+
| 方法 | 用途 | 契约版本 |
|
|
57
|
+
| -------------------- | -------------------------------------------------- | -------- |
|
|
58
|
+
| `w2m.status` | w2m 自身状态(插件/契约/SDK 版本、方法集、uptime) | 1.0 |
|
|
59
|
+
| `w2m.methods` | w2m 方法表自描述 | 1.0 |
|
|
60
|
+
| `w2m.fleet.snapshot` | 舰队快照(provider 注入) | 1.0 |
|
|
61
|
+
| `w2m.devices.list` | 设备列表(provider 注入) | 1.0 |
|
|
62
|
+
| `w2m.tasks.list` | 任务列表(provider 注入) | 1.0 |
|
|
63
|
+
|
|
64
|
+
**不伪造数据**:没有注入 provider 的方法返回 `handler-threw`,消息里带 `w2m-service-absent`,
|
|
65
|
+
而不是返回空数组冒充成功。
|
|
66
|
+
|
|
67
|
+
## 4. 传输层与兜底顺序
|
|
68
|
+
|
|
69
|
+
| 优先级 | 传输 | 场景 | 失败后 |
|
|
70
|
+
| ------ | ---------- | ----------------------------------------------- | -------- |
|
|
71
|
+
| 1 | **local** | 同一个 Host 的 DSH 服务(零网络) | → http |
|
|
72
|
+
| 2 | **http** | 对端 HTTP 契约端点(Bearer token) | → file |
|
|
73
|
+
| 3 | **file** | 同一文件系统 outbox/inbox(HTTP 被挡/重启窗口) | → legacy |
|
|
74
|
+
| 4 | **legacy** | 直接调用 3.1.x 旧服务键(`fangForker` 等) | 明确失败 |
|
|
75
|
+
|
|
76
|
+
- 传输超时 / HTTP 非 2xx / 不可用 → 自动下一层;
|
|
77
|
+
- **业务错误不降级**(例如 `ticket-invalid`),避免重复副作用;
|
|
78
|
+
- `unsupported-method` / `unsupported-version` / `retryable` → 下一层;
|
|
79
|
+
- 每次尝试记录在 `router.attempts()`,状态可观测。
|
|
80
|
+
|
|
81
|
+
### w2m 作为服务端
|
|
82
|
+
|
|
83
|
+
- local:提供 `w2mContract` 服务;同宿主对端(fang/master 的 local 传输)默认找 `fangContract`,
|
|
84
|
+
本插件在该名字**缺席时补位**、已存在时**不覆盖**(不与瓮城抢服务键)。
|
|
85
|
+
- http:`POST /api/w2m/contract/invoke`、`GET /api/w2m/contract/hello`、
|
|
86
|
+
`GET /api/w2m/contract/health`。w2m 不占用 fang 的 `/fang/api/contract/*`。
|
|
87
|
+
- file:配置 `fileDir` 时挂 SDK 的 file 契约服务端。
|
|
88
|
+
|
|
89
|
+
### w2m 作为客户端
|
|
90
|
+
|
|
91
|
+
`createW2mContractClient({ctx, config})` 直接复用 SDK 路由,可调用任意一方的方法;
|
|
92
|
+
`config` 支持 `httpBaseUrl` / `httpToken` / `pathPrefix` / `fileDir` / `order` / `timeoutMs`。
|
|
93
|
+
|
|
94
|
+
## 5. 演进操作手册
|
|
95
|
+
|
|
96
|
+
**新增一个 w2m 方法(MINOR,不需要双方同时升级):**
|
|
97
|
+
|
|
98
|
+
1. 在 `W2M_METHODS` 加常量、在 `W2M_METHOD_VERSION` 标版本、在 `createW2mContractServer` 的
|
|
99
|
+
`handlers` 里实现;
|
|
100
|
+
2. 更新本文与方法表测试;
|
|
101
|
+
3. 升插件 MINOR;契约版本按需 `1.0` → `1.1`(方法表新增属于契约 MINOR)。
|
|
102
|
+
|
|
103
|
+
**改一个方法的语义(MAJOR):**
|
|
104
|
+
|
|
105
|
+
1. 升 `W2M_CONTRACT_VERSION` 的 MAJOR;
|
|
106
|
+
2. 同时保留 `w2m.foo`(旧语义)与 `w2m.foo@2`(新语义)至少一个 MINOR 周期;
|
|
107
|
+
3. 两侧逐步升级依赖后删除旧方法。
|
|
108
|
+
|
|
109
|
+
**升级契约 SDK:**
|
|
110
|
+
|
|
111
|
+
1. `npm pack @twinsearth/dsh-fang-contract@<版本>`;
|
|
112
|
+
2. 覆盖 `src/interop/contract/`;
|
|
113
|
+
3. 重新生成 `PROVENANCE.md`;
|
|
114
|
+
4. `npm run verify:contract`(逐字节校验,pre-flight 之外也有测试覆盖)。
|
|
115
|
+
|
|
116
|
+
## 6. 文件地图
|
|
117
|
+
|
|
118
|
+
| 文件 | 作用 |
|
|
119
|
+
| ------------------------------------ | ------------------------------------------------------ |
|
|
120
|
+
| `src/interop/index.mjs` | w2m 方法表、服务端/客户端工厂、HTTP 路由、`apply` |
|
|
121
|
+
| `src/interop/contract/` | 契约 SDK 1.1.1 逐字节副本(含 LICENSE、PROVENANCE.md) |
|
|
122
|
+
| `src/interop/contract/PROVENANCE.md` | 来源、版本、每文件字节数与 sha256 |
|
|
123
|
+
| `scripts/check-contract-vendor.mjs` | 副本完整性校验 |
|
|
124
|
+
| `lib/contract.js` | 子插件入口(`@twinsearth/w2m-dsh-plugin/contract`) |
|
|
125
|
+
| `test/contract-interop.test.mjs` | 方法表/信封/路由/四级降级/副本一致性测试 |
|
|
126
|
+
| `cordis.patch.yml` | `dsh-w2m-contract` 行(默认启用) |
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "3.2.1",
|
|
3
|
+
"case": "RELEASE-3210",
|
|
4
|
+
"kind": "positive",
|
|
5
|
+
"machines": [
|
|
6
|
+
"b53a37f4-1464-4512-9259-33a7df091e3e"
|
|
7
|
+
],
|
|
8
|
+
"executed_at": "2026-11-14T07:12:44.822Z",
|
|
9
|
+
"executor": "lead",
|
|
10
|
+
"task_id": "pre-release-3210",
|
|
11
|
+
"requirement": "v3.2.1: restore the decision-center client UI surfaces (right-edge floating inbox badge + drawer, GPW inbox button, sidebar entry, AI settings) that v3.0.0/v3.2.0 shipped without, and prove them in a real DSH page",
|
|
12
|
+
"commands": [
|
|
13
|
+
[
|
|
14
|
+
"node",
|
|
15
|
+
"--test",
|
|
16
|
+
"--test-force-exit",
|
|
17
|
+
"test/client-bundle.test.mjs"
|
|
18
|
+
],
|
|
19
|
+
[
|
|
20
|
+
"node",
|
|
21
|
+
"--test",
|
|
22
|
+
"--test-force-exit",
|
|
23
|
+
"test/claims.test.mjs"
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
"node",
|
|
27
|
+
"--test",
|
|
28
|
+
"--test-force-exit",
|
|
29
|
+
"test/loader-entry.test.mjs"
|
|
30
|
+
],
|
|
31
|
+
[
|
|
32
|
+
"node",
|
|
33
|
+
"scripts/pack.mjs"
|
|
34
|
+
]
|
|
35
|
+
],
|
|
36
|
+
"result": {
|
|
37
|
+
"outcome": "ok",
|
|
38
|
+
"assertions": {
|
|
39
|
+
"floating": "a fixed right-edge, vertically centred inbox badge with a per-level tone and unread count; clicking it opens a right-to-left drawer over a dimmed overlay",
|
|
40
|
+
"drawer": "the drawer groups tickets red/yellow/blue/green/gray, filters pending/all/archived and by level, resolves through the ticket options or a free-text answer, abandons a pending ticket, shows AI advice without applying it, folds long context, and closes on Escape or overlay click",
|
|
41
|
+
"data": "list comes from GET /api/w2m/decisions?includeArchived=1, live updates from the /events SSE stream with a 4s polling fallback; resolve/abandon/advise hit the ported routes",
|
|
42
|
+
"gpw": "the GPW panel header carries an inbox button on both surfaces (GPW settings page and GPW launcher panel); it opens the same drawer",
|
|
43
|
+
"sidebar": "a 收件箱 sidebar entry with an unread dot opens the same drawer, matching the Fang inbox seat",
|
|
44
|
+
"ai": "an AI settings panel posts provider/model/baseUrl/apiKey to /ai/config and /ai/test and reports the result, instead of leaving the advise button silently failing",
|
|
45
|
+
"proof": "real Chromium page probe (W2M_DECISION_PROBE=1) confirms the badge is in the DOM, a click opens the drawer with a 收件箱 title, close hides it, the sidebar entry exists and the GPW panel contains the inbox button",
|
|
46
|
+
"suites": "client-bundle 36/36 (18 seats, the inbox sidebar entry included); loader-entry 10/10 with the browser probe; claims 47 pass + 1 network skip",
|
|
47
|
+
"remaining": "not yet ported from Fang: keyboard navigation inside the drawer, per-level batch resolve, and full fork/producer panels; recorded in CHANGELOG as follow-up work"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "3.2.2",
|
|
3
|
+
"case": "RELEASE-3220",
|
|
4
|
+
"kind": "positive",
|
|
5
|
+
"machines": [
|
|
6
|
+
"b53a37f4-1464-4512-9259-33a7df091e3e"
|
|
7
|
+
],
|
|
8
|
+
"executed_at": "2026-11-14T07:29:02.674Z",
|
|
9
|
+
"executor": "lead",
|
|
10
|
+
"task_id": "pre-release-3220",
|
|
11
|
+
"requirement": "v3.2.2: fix the v3.2.1 regression where the decision-inbox/queue-journal overlay locked the whole client (hidden attribute lost to an author display rule), and give the queue journal the persistent surfaces it lacked (right-edge floating badge + drawer, GPW button, always-visible composer dock chip), proven in a real DSH page",
|
|
12
|
+
"commands": [
|
|
13
|
+
[
|
|
14
|
+
"node",
|
|
15
|
+
"--test",
|
|
16
|
+
"--test-force-exit",
|
|
17
|
+
"test/client-bundle.test.mjs"
|
|
18
|
+
],
|
|
19
|
+
[
|
|
20
|
+
"node",
|
|
21
|
+
"--test",
|
|
22
|
+
"--test-force-exit",
|
|
23
|
+
"test/loader-entry.test.mjs"
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
"node",
|
|
27
|
+
"--test",
|
|
28
|
+
"--test-force-exit",
|
|
29
|
+
"test/claims.test.mjs"
|
|
30
|
+
],
|
|
31
|
+
[
|
|
32
|
+
"node",
|
|
33
|
+
"scripts/pack.mjs"
|
|
34
|
+
]
|
|
35
|
+
],
|
|
36
|
+
"result": {
|
|
37
|
+
"outcome": "ok",
|
|
38
|
+
"assertions": {
|
|
39
|
+
"badge": "a fixed right-edge 队列 badge below the inbox badge shows the number of saved journals and opens a drawer",
|
|
40
|
+
"drawer": "the drawer lists every saved journal newest-first with session key, update time, draft preview and per-row queued messages; each row restores the draft into the composer, restores the queue through POST /api/w2m/composer/queue (reporting session-not-live honestly) or clears the record; nothing is sent automatically",
|
|
41
|
+
"dock": "the composer dock no longer disappears when empty: it carries a 队列日志 chip that opens the drawer (the original Fang component returned null)",
|
|
42
|
+
"gpw": "the GPW panel header carries a 队列 N button next to the inbox button, opening the same drawer",
|
|
43
|
+
"data": "GET /api/w2m/composer/journal?list=1 lists entries, POST /api/w2m/composer/journal clears one, POST /api/w2m/composer/queue restores queued messages; the drawer refreshes on open and every 8s",
|
|
44
|
+
"id": "the composer dock registration id is w2m-queue-journal so it cannot collide with a simultaneously installed Fang queue-journal plugin",
|
|
45
|
+
"proof": "real Chromium page probe: POST and GET journal routes answer 200, the badge is in the DOM, a click opens the drawer whose title names 队列日志 and whose body contains the draft that was just saved, and the GPW panel contains the queue button; the conversation-scoped dock chip is reported as a diagnostic",
|
|
46
|
+
"suites": "queue-journal 4/4; client-bundle 36/36; loader-entry 10/10 with both probes; claims and layer model green",
|
|
47
|
+
"remaining": "the right sidebar tab (sidebar.right.pane.tab) is deferred: the host requires registering a tab type through the sidebarRightTabs service before a slot can be attached, and that integration needs its own stability pass; the surfaces it would duplicate are already reachable from the badge, the sidebar entry and the GPW button",
|
|
48
|
+
"overlay_lock": "the overlay and drawer toggle style.display instead of the hidden attribute, a [hidden]{display:none!important} rule backs it up, and the browser probe asserts the closed drawer/overlay compute to display:none and that elementFromPoint at the page centre is not inside the overlay",
|
|
49
|
+
"parity": "per-session 待选 floating button driven by a conversation.input.left seat; drawer keyboard nav (arrows/1-9/r/Esc); per-level batch resolve and abandon (red tickets refused by the host rule and greyed out); fork-aggregation and producer side panels opened from the drawer; session filter chip",
|
|
50
|
+
"parity_proof": "real Chromium probe: two tickets are ingested through /api/w2m/decisions/ingest, the drawer shows batch resolve/abandon and the side panel (opened, text non-empty), ArrowDown moves the selection, clicking batch resolve leaves no pending copy of either probe ticket, and the per-session relay button is reported as a diagnostic because the landing page has no active conversation"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|