@senguoyun/dsh-arkme 0.1.46 → 0.1.48
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/docs/dsh-remote-liveness.md +46 -0
- package/docs/member-directory-premerge-review.md +99 -0
- package/docs/member-directory-review.md +53 -0
- package/docs/plugin-release.md +9 -3
- package/docs/shared-conversation-members.md +61 -0
- package/docs/sidebar-local-first-review.md +104 -0
- package/docs/sidebar-local-first.md +61 -0
- package/lib/bundle-runtime.js +1 -1
- package/lib/client.js +68477 -66624
- package/lib/client.js.map +1 -1
- package/lib/extension-profile-restart-helper.js +5 -5
- package/lib/harness-model-client.js +36 -2
- package/lib/harness-session-client.js +124 -0
- package/lib/index.js +2837 -1532
- package/lib/{install-store-C1tAM346.js → install-store-bnyQDtYt.js} +1 -1
- package/lib/persistent-extension.js +1 -1
- package/lib/{persistent-runtime-DpNP9UKr.js → persistent-runtime-BOm3JEDw.js} +1573 -277
- package/lib/{plugin-updater-helper-BByro9U_.js → plugin-updater-helper-B0yVav4f.js} +1 -1
- package/lib/{plugin-updater-helper-BByw0ZyG.js → plugin-updater-helper-BbjKUV9T.js} +3 -3
- package/lib/plugin-updater-helper.js +1 -1
- package/lib/sdk.js +36 -2
- package/lib/types/arkme-service.d.ts +14 -0
- package/lib/types/arkme-service.d.ts.map +1 -1
- package/lib/types/chat-realtime.d.ts +6 -3
- package/lib/types/chat-realtime.d.ts.map +1 -1
- package/lib/types/client/ArkmeAvatar.d.ts +2 -1
- package/lib/types/client/ArkmeAvatar.d.ts.map +1 -1
- package/lib/types/client/ArkmeContactAddSurface.d.ts +2 -1
- package/lib/types/client/ArkmeContactAddSurface.d.ts.map +1 -1
- package/lib/types/client/ArkmeDSHBetaCommunityEntry.d.ts.map +1 -1
- package/lib/types/client/ArkmeDirectoryWindow.d.ts +6 -0
- package/lib/types/client/ArkmeDirectoryWindow.d.ts.map +1 -0
- package/lib/types/client/ArkmeGroupChatControls.d.ts +1 -1
- package/lib/types/client/ArkmeGroupChatControls.d.ts.map +1 -1
- package/lib/types/client/ArkmeMessageReadReceipt.d.ts.map +1 -1
- package/lib/types/client/ArkmePersistentShell.d.ts +1 -1
- package/lib/types/client/ArkmePersistentShell.d.ts.map +1 -1
- package/lib/types/client/ArkmeRecordingSurface.d.ts +3 -1
- package/lib/types/client/ArkmeRecordingSurface.d.ts.map +1 -1
- package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
- package/lib/types/client/ArkmeVirtualWorkspace.d.ts +2 -1
- package/lib/types/client/ArkmeVirtualWorkspace.d.ts.map +1 -1
- package/lib/types/client/DeepSeekHarnessSurface.d.ts +3 -1
- package/lib/types/client/DeepSeekHarnessSurface.d.ts.map +1 -1
- package/lib/types/client/api.d.ts +1 -1
- package/lib/types/client/api.d.ts.map +1 -1
- package/lib/types/client/avatar-image-store.d.ts +3 -2
- package/lib/types/client/avatar-image-store.d.ts.map +1 -1
- package/lib/types/client/bot-chat-directory-projection.d.ts +2 -0
- package/lib/types/client/bot-chat-directory-projection.d.ts.map +1 -1
- package/lib/types/client/bot-directory-preferences.d.ts +2 -0
- package/lib/types/client/bot-directory-preferences.d.ts.map +1 -1
- package/lib/types/client/chat-directory-store.d.ts +4 -0
- package/lib/types/client/chat-directory-store.d.ts.map +1 -1
- package/lib/types/client/conversation-members-store.d.ts +46 -0
- package/lib/types/client/conversation-members-store.d.ts.map +1 -0
- package/lib/types/client/harness-session-client.d.ts +5 -0
- package/lib/types/client/harness-session-client.d.ts.map +1 -0
- package/lib/types/client/interwoven-moments.d.ts +9 -0
- package/lib/types/client/interwoven-moments.d.ts.map +1 -1
- package/lib/types/client/message-read-receipt-store.d.ts +7 -1
- package/lib/types/client/message-read-receipt-store.d.ts.map +1 -1
- package/lib/types/client/realtime-client-events.d.ts.map +1 -1
- package/lib/types/client/recordings/ArkmeRecordingImportDialog.d.ts +4 -1
- package/lib/types/client/recordings/ArkmeRecordingImportDialog.d.ts.map +1 -1
- package/lib/types/client/redesign/contacts/CollapsibleDirectorySection.d.ts +3 -2
- package/lib/types/client/redesign/contacts/CollapsibleDirectorySection.d.ts.map +1 -1
- package/lib/types/client/redesign/contacts/ContactDirectoryAddDialog.d.ts +8 -0
- package/lib/types/client/redesign/contacts/ContactDirectoryAddDialog.d.ts.map +1 -0
- package/lib/types/client/redesign/contacts/ContactDirectorySurface.d.ts +13 -4
- package/lib/types/client/redesign/contacts/ContactDirectorySurface.d.ts.map +1 -1
- package/lib/types/client/redesign/contacts/ContactDirectoryToolbar.d.ts +7 -0
- package/lib/types/client/redesign/contacts/ContactDirectoryToolbar.d.ts.map +1 -0
- package/lib/types/client/redesign/contacts/ContactProfileDetail.d.ts +12 -2
- package/lib/types/client/redesign/contacts/ContactProfileDetail.d.ts.map +1 -1
- package/lib/types/client/redesign/contacts/ContactRemarkDialog.d.ts +9 -0
- package/lib/types/client/redesign/contacts/ContactRemarkDialog.d.ts.map +1 -0
- package/lib/types/client/redesign/contacts/ContactWorldList.d.ts +2 -0
- package/lib/types/client/redesign/contacts/ContactWorldList.d.ts.map +1 -1
- package/lib/types/client/redesign/contacts/DirectoryDetailPane.d.ts +3 -2
- package/lib/types/client/redesign/contacts/DirectoryDetailPane.d.ts.map +1 -1
- package/lib/types/client/redesign/contacts/contact-date.d.ts +3 -0
- package/lib/types/client/redesign/contacts/contact-date.d.ts.map +1 -0
- package/lib/types/client/redesign/contacts/contact-directory-search.d.ts +10 -0
- package/lib/types/client/redesign/contacts/contact-directory-search.d.ts.map +1 -0
- package/lib/types/client/redesign/contacts/contact-directory-state.d.ts +4 -1
- package/lib/types/client/redesign/contacts/contact-directory-state.d.ts.map +1 -1
- package/lib/types/client/redesign/contacts/contacts-tab-store.d.ts +6 -2
- package/lib/types/client/redesign/contacts/contacts-tab-store.d.ts.map +1 -1
- package/lib/types/client/use-arkme-avatar-image.d.ts +2 -1
- package/lib/types/client/use-arkme-avatar-image.d.ts.map +1 -1
- package/lib/types/client/use-conversation-members.d.ts +3 -0
- package/lib/types/client/use-conversation-members.d.ts.map +1 -0
- package/lib/types/dsh-remote/api-proxy-adapter.d.ts +1 -0
- package/lib/types/dsh-remote/api-proxy-adapter.d.ts.map +1 -1
- package/lib/types/dsh-remote/channel-manager.d.ts +9 -2
- package/lib/types/dsh-remote/channel-manager.d.ts.map +1 -1
- package/lib/types/dsh-remote/connection-diagnostics.d.ts +35 -0
- package/lib/types/dsh-remote/connection-diagnostics.d.ts.map +1 -0
- package/lib/types/dsh-remote/current-session-tool.d.ts +3 -0
- package/lib/types/dsh-remote/current-session-tool.d.ts.map +1 -0
- package/lib/types/dsh-remote/default-socket-factory.d.ts.map +1 -1
- package/lib/types/dsh-remote/desktop-session-presence.d.ts +21 -0
- package/lib/types/dsh-remote/desktop-session-presence.d.ts.map +1 -0
- package/lib/types/dsh-remote/dsh-event-contract.d.ts +2 -0
- package/lib/types/dsh-remote/dsh-event-contract.d.ts.map +1 -1
- package/lib/types/dsh-remote/host.d.ts +40 -6
- package/lib/types/dsh-remote/host.d.ts.map +1 -1
- package/lib/types/dsh-remote/live-event-batcher.d.ts +46 -0
- package/lib/types/dsh-remote/live-event-batcher.d.ts.map +1 -0
- package/lib/types/dsh-remote/protocol-v1.d.ts.map +1 -1
- package/lib/types/dsh-remote/realtime-transport.d.ts +19 -1
- package/lib/types/dsh-remote/realtime-transport.d.ts.map +1 -1
- package/lib/types/dsh-remote/types.d.ts +18 -2
- package/lib/types/dsh-remote/types.d.ts.map +1 -1
- package/lib/types/harness-embed-contract.d.ts +2 -0
- package/lib/types/harness-embed-contract.d.ts.map +1 -1
- package/lib/types/harness-embed-route.d.ts +4 -0
- package/lib/types/harness-embed-route.d.ts.map +1 -1
- package/lib/types/host-api.d.ts.map +1 -1
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/local-database.d.ts +11 -1
- package/lib/types/local-database.d.ts.map +1 -1
- package/lib/types/member-directory.d.ts +11 -0
- package/lib/types/member-directory.d.ts.map +1 -0
- package/lib/types/sdk/index.d.ts +24 -3
- package/lib/types/sdk/index.d.ts.map +1 -1
- package/lib/types/services/bot-service.d.ts +3 -0
- package/lib/types/services/bot-service.d.ts.map +1 -1
- package/lib/types/services/chat-realtime-service.d.ts +1 -0
- package/lib/types/services/chat-realtime-service.d.ts.map +1 -1
- package/lib/types/services/chat-service.d.ts +14 -1
- package/lib/types/services/chat-service.d.ts.map +1 -1
- package/lib/types/services/contact-directory-service.d.ts +3 -0
- package/lib/types/services/contact-directory-service.d.ts.map +1 -1
- package/lib/types/services/conversation-directory-service.d.ts +72 -0
- package/lib/types/services/conversation-directory-service.d.ts.map +1 -0
- package/lib/types/services/desktop-attention-bridge.d.ts +6 -1
- package/lib/types/services/desktop-attention-bridge.d.ts.map +1 -1
- package/lib/types/services/group-service.d.ts.map +1 -1
- package/lib/types/services/media-service.d.ts +1 -0
- package/lib/types/services/media-service.d.ts.map +1 -1
- package/lib/types/services/service.d.ts +11 -0
- package/lib/types/services/service.d.ts.map +1 -1
- package/lib/types/services/source-service.d.ts +3 -0
- package/lib/types/services/source-service.d.ts.map +1 -1
- package/lib/types/services/unmarked-speaker-service.d.ts.map +1 -1
- package/lib/types/tools/business/conversation/index.d.ts.map +1 -1
- package/lib/types/tools/business/conversation/list-sources.d.ts +1 -0
- package/lib/types/tools/business/conversation/list-sources.d.ts.map +1 -1
- package/lib/types/tools/business/conversation/member-pages.d.ts +2 -0
- package/lib/types/tools/business/conversation/member-pages.d.ts.map +1 -0
- package/lib/types/tools/business/conversation/read-receipts.d.ts.map +1 -1
- package/lib/types/tools/business/index.d.ts.map +1 -1
- package/lib/types/tools/ports/conversations.d.ts +13 -1
- package/lib/types/tools/ports/conversations.d.ts.map +1 -1
- package/lib/types/tools/registry/registrar.d.ts.map +1 -1
- package/lib/types/types.d.ts +66 -2
- package/lib/types/types.d.ts.map +1 -1
- package/package.json +7 -6
- package/lib/{bundle-runtime-BTzRlc6V.js → bundle-runtime-CE73h3vS.js} +1 -1
- package/lib/{profile-bundle-policy-CvojZuTD.js → profile-bundle-policy-DLXDyohJ.js} +1 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# DSH 远控连接恢复
|
|
2
|
+
|
|
3
|
+
远控由登录账号作用域内的 Host 持有,与桌面是否显示 DSH 会话无关。连接只有完成 WebSocket ready、命令频道订阅和 Host lease 注册才可用。完整工作区/会话快照在连接完成后异步同步;账号及会话所有权验证仍须先完成。
|
|
4
|
+
|
|
5
|
+
Transport 默认在最后一次有效入站协议帧/服务端 Ping/Pong 后 45 秒终止失活 Socket,不以发包、HTTP 同步或 `readyState=OPEN` 延长该期限。默认 `ws` adapter 使用公开控制帧事件;自定义 socket factory 必须提供 `subscribeHeartbeat`、`terminate`;缺失时明确拒绝能力,不带着不完整的保活语义运行。
|
|
6
|
+
|
|
7
|
+
每次连接尝试总预算 30 秒,注册控制面单步最多 10 秒;所有取消和回调带连接 generation,旧订阅退出不会取消新订阅。连接被同身份替换时旧 Host 退出自动重连,避免双实例互相抢占。其他可重试错误复用已有 1–30 秒退避,稳定 60 秒后重置。
|
|
8
|
+
|
|
9
|
+
`lifecycle.get` 由受控桥的 `capabilities.get` 显式声明:原生生命周期取样不阻塞账号轮询;已提供该能力时,resume generation 变化使旧连接重新验证。未提供原生能力的 Harness 通过 Transport 入站活性确认连接,不根据错误文案猜测版本。缓存中的 suspended 提示不阻止连接恢复。该桥不承载新的业务数据或模型工具。
|
|
10
|
+
|
|
11
|
+
## 诊断部署
|
|
12
|
+
|
|
13
|
+
关键事件默认进入 Harness 日志,含 user/client/runtime、attempt、connection/lease generation、HTTP request_id 及 App/Harness/插件版本。逐帧日志仍受 `ARKME_DSH_REMOTE_DIAGNOSTICS` 控制,不默认开启。
|
|
14
|
+
|
|
15
|
+
`dshRemoteSentryDsn` 配置为该环境的 Sentry 项目 DSN 后,Host 通过隔离的 NodeClient 上报连续失败/恢复;不安装全局 Node 监控,不记录错误原文、凭据、工作目录或对话内容。未配置时保留本地日志与有界诊断缓存,不声称已具备远程 Sentry 交付。
|
|
16
|
+
|
|
17
|
+
一次 episode 在三次失败或持续 60 秒后汇总一次,恢复另记一次。最近状态最多 32 条,事件最多 24KiB;待发送事件最多 100 项、每项 24KiB、保留七天,落盘不超过 3MiB。持久写入合并,发送由现有 Host poll 驱动,最多一个 flight、30 秒失败重试间隔。账号切换清除不属于当前账号的积压。
|
|
18
|
+
|
|
19
|
+
保活改动的 Tools/SDK:未新增业务能力,现有 remote.getStatus 和业务操作保持契约。UI:手机消费独立连接失败状态;桌面业务不因远控故障重启 Harness。Client:lifecycle.get 增量桥需配套测试;旧客户端不支持时降级。
|
|
20
|
+
|
|
21
|
+
验证命令:`pnpm test`、`pnpm run typecheck`、`pnpm run build`。`tests/dsh-remote-socket-liveness.integration.test.ts` 使用本地真实 WebSocket 验证无关闭通知的静默失活与替换握手;假时钟测试覆盖 deadline、资源释放、旧 generation 和慢快照。正式交付还需用目标平台和实际 Release Set 验收睡眠/网络恢复及 Sentry 入站。
|
|
22
|
+
|
|
23
|
+
账号切换使旧后台快照的 generation 和取消信号失效,迟到结果不得完成新账号的投影或污染告警。诊断配置无效只降级日志;缓存加载重建允许字段,异步入队与发送以账号 generation 隔离,日志收尾具有总时间上限。
|
|
24
|
+
|
|
25
|
+
## 移动端入口跟随桌面当前会话
|
|
26
|
+
|
|
27
|
+
`session.current` 是 Host 声明的增量只读能力。内嵌 DSH 页面通过公共 `ISessions.list.current` 读取选择,Arkme 自己的 surface 标记提供当前账号和页面可见性;独立小模块只进入 iframe Boot graph,不加载外层业务 UI。普通聊天、日历、登录页或没有当前会话时清空选择。列表更新但选择未变不会发请求。可见会话每 10 秒续期,窗口状态最多 8 个、按 Host 单调时钟 30 秒过期,无业务历史或排序字段写入;账号退出清空;页面关闭或刷新通过同源 beacon 清空选择,页面恢复重新读取公共选择状态。
|
|
28
|
+
|
|
29
|
+
Host 的 `currentSession` 通过只读 Session 归属查询及公共会话列表验证归属、目录、归档和 subagent 状态,不认领未知会话,不改写归属文件。`session.current` Realtime 请求、SDK `currentDesktopSession()` 和只读工具 `arkme_current_dsh_session` 共用这个方法。Browser `remote.reportCurrentSession` 只报告真实页面状态,要求同源和当前账号;不向 Tool 提供伪造用户页面选择的写入口。
|
|
30
|
+
|
|
31
|
+
手机只在普通 DSH 入口执行一次有 2 秒总预算的读取,优先查询上次使用的在线 Runtime;没有可用选择时检查其他支持该能力的在线 Runtime。旧 Host 不声明能力时不发送新请求。超时、失效、账号变化均回退本地上次会话。显式会话路由、指定目录新对话以及用户手动导航不跟随;进入后不持续跟随桌面跳转。
|
|
32
|
+
|
|
33
|
+
手机入口等待预算只限制查询等待和应答 waiter,保留原有 30 秒远端命令执行窗口。加载期间输入区可用;输入、焦点、菜单、侧栏和应用退后台都会撤销自动导航,撤销标记持续到首次页面安装前,已展示的会话仍按原有 generation 处理后续刷新。
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## 流式投递批处理与耗时诊断
|
|
37
|
+
|
|
38
|
+
Host 的单一消费者在上批完成后,从当前缓冲取下一批。空闲时 assistant/chunk 最多合并等待 40ms,语义事件或达到 50 项/32KiB 后立即唤醒消费者;同会话按 canonical seq 保序,不让工具结果或 turn/end 越过前文。不同会话每批轮转,共享现有 ChannelManager 的串行发送和分片协议。单条超出 32KiB 的事件沿用既有分片路径。
|
|
39
|
+
|
|
40
|
+
缓冲最多 4096 项、64MiB 序列化 payload、256 个待处理会话;这些是缓冲容量,不是进程 RSS 上限。容量满后保留会话序号水位,用现有公开 Host history 逐段读取 canonical 事件,再依次归档、投递;读取受既有 64MiB 响应预算约束,失败不推进水位,2 秒后重试准备。超过会话容量显式记录投影错误,原始 DSH 历史仍由既有 owner 持有。账号/Runtime 队列关闭时清除缓冲和 timer,迟到结果不能投递到新作用域。
|
|
41
|
+
|
|
42
|
+
保留原有 capture-before-send 顺序及归档失败的 historySyncError 降级,不把实时传输成功等同于持久化成功。发送重试仍只由 ChannelManager 按稳定帧 ID 最多尝试三次;重试耗尽后记录投影错误,由既有历史恢复补齐,不重新执行已完成的 Turn capture。桌面 Agent 的事件生产不等待归档或 ACK。
|
|
43
|
+
|
|
44
|
+
默认 `live_delivery_window` 每 10 秒最多一条,复用 Host poll 和现有日志出口。含 user_id/runtime_ref、batch_count/event_count/incomplete_batches/replayed_batches、payload_bytes、pending_entries_max/pending_bytes_max,以及 queue_ms、ownership_ms、capture_ms、channel_queue_ms、publish_ack_ms 的 sum/max。capture_ms 包含 outbox 队列等待及落盘调用;publish_ack_ms 包含分片、重试和等待确认,不是单次网络 RTT。replay 的 queue_ms 是原积压起点计算的保守年龄。incomplete_batches 表示未完整确认的批次;event_count 是尝试处理数量,不是已确认数量。指标使用单调时钟;不包含事件正文或凭据,不挤占连接故障的 32 条上下文,不新增 Sentry 连接事故。
|
|
45
|
+
|
|
46
|
+
受控性能回归:`pnpm exec vitest run tests/dsh-live-batching-benchmark.test.ts --disableConsoleIntercept`。它对比相同生成流和模拟归档/ACK,不能替代实际 Profile、设备和网络验收。
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# 成员目录合并前逐场景审查(2026-09-08)
|
|
2
|
+
|
|
3
|
+
## 结论
|
|
4
|
+
|
|
5
|
+
本轮审核了当前任务分支的完整改动,而非仅检查上一轮修复。逐项核对下表场景的触发入口、Host/服务端事实、缓存写入、UI 消费与失败恢复,复现并修复 6 类遗漏。随后按用户要求改为有内容时静默刷新。未新增业务状态机、依赖、轮询或独立成员数据源。
|
|
6
|
+
|
|
7
|
+
本报告列出已知需求及此前问题的完整核对清单。实际执行、代码链路确认与未执行环境明确区分;不将“没有发现更多问题”写成“任何场景绝无缺陷”。
|
|
8
|
+
|
|
9
|
+
## 本轮发现与修复循环
|
|
10
|
+
|
|
11
|
+
| 编号 | 触发与原问题 | 根因位置与最终修复 | 复现/复验 |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
| F1 / P1 | 已退群、被移出、群停用后仍保留成员缓存 | Chat 实际返回 2001/2002;原判断只覆盖 403/1004。`member-directory.ts::invalidatesMemberSnapshot` 在成员读合同内统一识别,Host 与 Store 同时清除缓存 | Go Handler 精确断言 2001/2002;`member-directory-final-review` 与真实 Host/SQLite contract 在修复前失败、修复后通过 |
|
|
14
|
+
| F2 / P1 | 成员变更后新读取复用变更前未结束请求,可把旧成员再次写回 | `ChatService.memberRead` 去重键加入已有 memberCacheEpoch;缓存写入继续检查同一代次。无需新队列、锁或删除标记 | contract 中旧请求挂起→变更→清缓存→新请求,修复前上游只启动一次,修复后新读取独立启动且最终为空 |
|
|
15
|
+
| F3 / P2 | 旧缓存群主角色影响撤回、移除和退群历史入口;等待自己排到后续页会延迟管理操作 | Handler 复用访问校验已经读取的当前成员,逐页返回 self_role;Store 的 selfRole 仅由这些远端访问事实更新,缓存不填充,失效时归 unknown。Sidebar 不再从缓存行找角色 | 首页面含无关行、自己的行在后页,仍及时得到真实角色;缓存显示为 owner 时权限角色为 member。写权限仍由后端再次校验 |
|
|
16
|
+
| F4 / P2 | 已读详情切后台再回来,若角标不在 visible 集合就不恢复刷新 | `MessageReadReceiptStore.reconcile` 先刷新详情观察者,再处理可见摘要;复用既有定时器去重 | 先补“隐藏期间零刷新、回来只刷新一次”的失败测试,再修复通过 |
|
|
17
|
+
| F5 / P2 | 第一页 50 人被显示成群总人数,覆盖已知 500 人 | Sidebar 仅用无错误的完整目录更新总人数;刷新期间使用原有群投影人数。Store 刷新开始置 complete=false | 实际 Surface 测试:后页挂起仍显示 500 人且输入框可用,完整结果到达才更新总数 |
|
|
18
|
+
| F6 / P1 | 其他设备拉人/重新入群,当前打开的目录不刷新 | 实际 Chat→IM 已有 t=24;插件只接 t=27。新增独立 t=24 解码与 members-invalidated 事件,复用公共 Store;t=27 继续单独失效退群历史 | 解码、真实 SSE 流去重、Host 投影、Browser 路由测试;打包实例从 499 人收到 t=24 后自动恢复 500 人,无需重开抽屉 |
|
|
19
|
+
|
|
20
|
+
用户追加要求:删除成员标题“更新中”、已读详情“正在更新”和行内“正在读取资料”。保留旧行静默更新;未知统计留空并保留行高,不伪造零值。首次无数据仍有加载反馈,明确失败仍可重试。相关 UI 测试覆盖刷新期间标题不变、旧行不清空、不出现状态行。
|
|
21
|
+
|
|
22
|
+
## 场景→代码→证据清单
|
|
23
|
+
|
|
24
|
+
代码路径以插件仓为根;Chat 路径明确注明。表中的测试均为仓内可直接运行的测试文件。
|
|
25
|
+
|
|
26
|
+
| 场景 | 实际链路/事实边界 | 对应证据 |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| S01 首次冷开,无本地缓存 | `use-conversation-members`→Store.ensure→loadPages;缓存和第一页并行,未命中不延迟网络 | `conversation-members-paging.test.ts`、打包冷启动 |
|
|
29
|
+
| S02 200/500 人逐页读取 | Host page→Chat handler→repository limit+1/user_id cursor;每页最多50(接口最大100),非前端全量切片 | `conversation-members-loading.test.ts`;Chat `member_page_test.go` 500人10页与 explain |
|
|
30
|
+
| S03 会话、抽屉、@和已读详情同时消费 | 同账号/群复用 entry/pending;UI 仅订阅快照 | `conversation-members-store.test.ts` 同时订阅与重开抽屉仅一次遍历 |
|
|
31
|
+
| S04 慢资料、两个任务占满 | loadPages 的生产者继续分页;drain 只限制资料并发为2 | `member-directory-review.test.ts` 两个任务阻塞时150人3页已可见;500人真实协调器负载测试 |
|
|
32
|
+
| S05 基础名片、真实统计与头像失败 | mergeMemberFacts 保留展示;statsKnown=false 表示尚未读取;mergeMemberPresentation 按成员保留不可用头像,更新已取得姓名/统计 | `member-directory-review.test.ts`、`conversation-members-cache.test.ts` |
|
|
33
|
+
| S06 当前姓名与缓存姓名不同 | 已读当前姓名优先;displayNameIsCurrent=false 时才订阅目录补展示 | `member-directory-contract.test.ts`、`message-read-receipt-detail.test.tsx` |
|
|
34
|
+
| S07 私人备注与公开@名称 | projectChatMembers→resolveChatMemberMentionDisplayNames;@正文用 mentionDisplayName,私人备注不能填公开名称 | `conversation-send-directory.test.tsx`、`mention-metadata.test.ts`;Chat member list/by IDs 公开名片测试 |
|
|
35
|
+
| S08 Bot、真人、@所有人 | Bot 候选独立 owner;真人需合法 mentionRef;all 是群提及范围而非某个人 | `conversation-send-directory.test.tsx` 群/私聊候选与发送测试 |
|
|
36
|
+
| S09 同名成员、未知邀请人 | join events 按 memberRef/eventId 合并,未知邀请人不以名字合并身份 | `member-directory-review.test.ts` 同名保留两人 |
|
|
37
|
+
| S10 跨进程重开、换端口 | SQLite 环境目录+user_id+群;当前签名验证后恢复,正常远端请求照发 | `conversation-members-cache.test.ts` reopen/空格路径;打包进程重启验收 |
|
|
38
|
+
| S11 缓存晚于远端、晚于新一轮重试 | current/revision/remoteProgress 阻止晚到缓存覆盖远端 | `conversation-members-paging.test.ts`、Store stale-result 测试 |
|
|
39
|
+
| S12 缓存损坏、过期、超容量、磁盘不可用 | readConversationMembers 白名单/版本/时间/大小校验;读写失败按miss;容量淘汰不截断伪造完整名单 | `conversation-members-cache.test.ts`、`member-directory-review.test.ts`;容量上限代码核对 |
|
|
40
|
+
| S13 旧缓存表升级 | 既有表增加 payload_bytes,一次性迁移;旧内容不重写,未知版本不进入渲染 | `conversation-members-cache.test.ts` 连续重开两次,验证旧JSON及无关记录保持不变 |
|
|
41
|
+
| S14 缓存引用失效与并发修复 | cachedSourceMembers 验证引用;forgetCachedMembers 仅移除失败refs,不清除并发新快照 | `member-directory-contract.test.ts`、cache 精确移除测试 |
|
|
42
|
+
| S15 首次/后页失败、超时、重试 | 失败保留已有行;重试复用公共ensure;重复/缺失游标失败,单遍历最多200页 | paging/store 错误、游标、后台重试测试;普通读取协调器超时/取消测试 |
|
|
43
|
+
| S16 畸形数组、重复ID、跨群页、伪造游标 | Host 验证来源/顺序/状态/limit;Store 校验更新合同,畸形响应不能成为空列表删除 | `member-directory-contract.test.ts`、store/paging 校验测试 |
|
|
44
|
+
| S17 未出现于实时遍历的旧成员 | 末页后以 members/by-user-ids 明确核验;缺席自身不等于删除 | `conversation-members-paging.test.ts` missing cached member 场景 |
|
|
45
|
+
| S18 退群/被移除/解散/授权撤销 | 服务端每页重新验证;Host/Store 识别真实2001/2002并清除;迟到cache及读取不能恢复 | Chat page Handler;`member-directory-final-review.test.ts`、contract |
|
|
46
|
+
| S19 移除成功/失败、阻止再加入 | removeGroupMember 先验证服务端回执,再失效代次和应用removedRef;失败不乐观删除。离群/解散成功后清群缓存 | `ChatService.removeGroupMember`、`GroupService.leaveGroup/dissolveGroup`;既有群治理与新epoch测试 |
|
|
47
|
+
| S20 拉人、已在群、重新激活、仅发邀请 | GroupService 按 add outcome 区分 added/already_member/reactivated;invite_sent不创建成员事实;UI onAdded仅要求回查 | `GroupService.addGroupMembers`、`group-settings-menu.test.tsx`及既有 GroupService 测试 |
|
|
48
|
+
| S21 变更时有共享旧请求 | 新读取去重键使用新代次;旧结果只能完成原调用,不能回填新缓存 | contract 真实协调器先红后绿场景 F2 |
|
|
49
|
+
| S22 资料迟到、成员已被移除 | applyMemberUpdate 只有 membership可创建,presentation只能更新已有实体;无额外墓碑状态 | `conversation-members-cache.test.ts` no resurrection |
|
|
50
|
+
| S23 角色缓存过期/自己位于后页 | self_role来自每页访问上下文,展示role不授予管理权限;GroupSettings仍读自身policy合同 | final-review 首页面角色测试、group-settings UI测试 |
|
|
51
|
+
| S24 快速切群、sourceRef轮换 | key按账号与sourceKey;不同群不复用旧行;能力引用变化取消旧工作并重新读取 | store/hook、`conversation-send-directory.test.tsx`跨群晚到/人数测试 |
|
|
52
|
+
| S25 切账号/环境、旧组件订阅 | activateAccount为生命周期owner;subscribe不切账号,未激活订阅不发请求 | `member-directory-review.test.ts`、read-receipt scope测试 |
|
|
53
|
+
| S26 运行实例变更、前后台、最后订阅者卸载 | reset/cancel/foreground和AbortSignal贯通;一个订阅者退出不杀其他订阅者 | store、cache reopen、avatar visibility与receipt store测试 |
|
|
54
|
+
| S27 重复SSE、其他设备加入 | t=24解码→ChatRealtime notice→members-invalidated→公共Store.invalidate;重复事件去重,历史窗口不受此类型影响 | `chat-realtime.test.ts`、`services/chat-realtime-service.test.ts`、`realtime-client-events.test.ts`;打包SSE回查 |
|
|
55
|
+
| S28 退群历史通知 | t=27保留eventId/occurredAt给member-event owner;成员目录只回查,不把事件直接拼成成员或已读数据 | 同上+t=27既有member-events/cache/hook测试 |
|
|
56
|
+
| S29 已读参与范围与当前成员名单不同 | messageReadReceiptDetail先验证当前用户消息与服务端参与集合;目录只提供展示,不计算谁读了或总参与人数 | `member-directory-contract.test.ts`、message-read-receipt系列与既有服务测试 |
|
|
57
|
+
| S30 详情/摘要并发、后到旧摘要 | detail更新summary后,旧摘要以loading entry身份校验跳过;详情single-flight不因TTL过期重复发起 | `message-read-receipt-store.test.ts` |
|
|
58
|
+
| S31 开着详情切后台/回前台 | 停掉观察者定时器;reconcile独立恢复详情,即使无可见角标 | 同上F4补充回归 |
|
|
59
|
+
| S32 消息输入、连续发送、失败恢复 | canSend只依赖草稿/admission/preparing;成员refreshing不进入发送门禁,partial rows不替换总人数 | `conversation-send-directory.test.tsx`连续发送与分页输入场景、打包输入并清空草稿 |
|
|
60
|
+
| S33 500行头像与组件卸载 | IntersectionObserver只订阅可见avatarRef,隐藏/卸载释放;不增加图片owner | `member-avatar-visibility.test.tsx`:0可见0加载,8可见8加载,500 observer释放 |
|
|
61
|
+
| S34 无感刷新 | 既有行保留,无“更新中”状态行;未知统计空白占位,真实失败仍可重试 | `group-settings-menu.test.tsx`、`message-read-receipt-detail.test.tsx`、打包延迟场景 |
|
|
62
|
+
| S35 Tools/SDK/UI一致与旧接口 | 新能力共用ChatService;正式Tools注册调用;仓外SDK只用公开导出;旧完整接口继续服务旧调用方,新UI无全量fallback | 正式DSH contract、仓外tgz consumer、打包UI、完整测试 |
|
|
63
|
+
|
|
64
|
+
## 不同概念的边界
|
|
65
|
+
|
|
66
|
+
成员身份不等于显示名称;成员加入失效通知不等于退群历史记录;当前群成员不等于消息已读参与集合;缓存展示角色不等于已验证访问角色;已加载页数不等于群总人数;头像失败不等于整批资料失败;邀请已发送不等于已经入群;通用业务2002冲突不等于任何业务都应删除数据——新增判断仅用于两个已核对的成员读取接口。
|
|
67
|
+
|
|
68
|
+
公共成员源没有双加载实现或自动全量降级;没有新服务、依赖或持久化状态机。必要的scope/revision、bounded queue及schema迁移继续由原owner持有。
|
|
69
|
+
|
|
70
|
+
## 验证记录与边界
|
|
71
|
+
|
|
72
|
+
最终测试数量、集成快照和打包结果见本节末尾补充。真实 Mongo 场景包括500人10页、已有索引、无COLLSCAN/SORT、inactive rows、50+1边界、翻页新增、空尾页、取消。官方DSH 0.1.1-rc.2三个成员Tools实际注册/执行/卸载,仓外SDK从tgz编译并调用,首个分页即可返回selfRole。
|
|
73
|
+
|
|
74
|
+
打包隔离实例的500人冷启动:10次基础分页、10次资料补齐;正常输入不受阻。进程重启并改变端口:成员请求560ms发出,缓存580ms返回,644ms时500行可见(相对于导航;相对于成员请求84ms),当时2秒延迟的远端尚未返回;随后改名/退群收敛499人。向同一SSE连接投递真实形状t=24后,无需重开抽屉自动恢复500人。
|
|
75
|
+
|
|
76
|
+
合成远端未实现部分Arko/内测群/目录偏好能力,其预期502不算成员场景成功证据,也不算全产品E2E。正式服务器MQ→IM开关、生产流量、Windows/Linux实机未执行,不能据本机模拟保证生产时延或跨平台结果。
|
|
77
|
+
|
|
78
|
+
Docker不可用;后端全量Docker E2E未通过执行。Mongo全包有前轮已在干净基线复现的既有失败;本轮实际原生Mongo新场景全部通过,未宣称后端全量全绿。没有推送、PR、生产发布或用户业务数据操作。
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
本轮静默展示打包复验:在实际成员分页仍有1个请求在途时,页面已显示500行,标题严格为“协作者(500)”,无“更新中/正在更新/正在读取资料”。预览沿用本任务隔离Profile和合成账号,保留在本机49773供用户继续查看,未替换正式客户端。
|
|
82
|
+
|
|
83
|
+
当前分支源码全量4681项通过、5项跳过;类型检查与构建通过。Chat `go test ./gin/api ./internal/chat` 两个受影响包全量通过,原生Mongo新分页专项及go vet另行通过。OpenSpec strict通过。
|
|
84
|
+
|
|
85
|
+
集成记录:先以6429e25为目标完成4733项测试;最终快照采用随后更新的5581c826。该目标新增Sentry依赖,原复用依赖目录不能代表目标依赖,已切换为临时目录独立安装后复验;没有修改任务分支依赖或锁文件。最终结果在下方记录。
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
最终集成门禁补充:5581c826合并快照的4790项测试通过(5项跳过),但独立依赖安装后确认该目标自身存在3处Sentry transport回调隐式any;该文件与目标基线完全相同,属于目标已有类型问题。当前任务分支已无冲突合入此精确基线,并仅给transport回调补入从makeNodeTransport推导的参数与返回类型,消除3处诊断,不改运行行为。版本、依赖和锁文件随官方基线同步,任务没有另行变更其值。
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
### 最终收口结果
|
|
92
|
+
|
|
93
|
+
- 当前任务分支已包含官方dev 5581c826,完整测试 **4790通过 / 5跳过**;typecheck、build、可执行入口校验通过。Sentry类型补齐后的相关85项测试及完整回归均通过。
|
|
94
|
+
- Chat受影响两个包全量测试、真实Mongo分页专项、go vet通过;OpenSpec strict通过。没有遗留已确认而未处理的本任务finding。
|
|
95
|
+
- 最终官方DSH安装产物沿用目标基线的0.1.47版本;正式Tools合同和仓外SDK编译/调用通过。SDK实际返回500缓存成员、50人第一页、selfRole=owner及有效游标。
|
|
96
|
+
- 最终预览实测标题为“协作者(500)”,500行可见,后台分页在途时无更新提示;预览继续保留,便于用户操作。
|
|
97
|
+
- 相对5581c826,package.json、锁文件、根README均无任务差异。没有远端push、PR或合并/部署操作;同步开发基线只发生于本任务本地分支。
|
|
98
|
+
|
|
99
|
+
在以上验证范围内,当前成员目录改动具备合并条件。发布顺序仍为Chat分页及self_role合同先到位,再接入插件;正式MQ/IM开关、生产负载及Windows/Linux实机验收不在本机模拟结果内。
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# 群成员与已读详情审查记录(2026-09-08)
|
|
2
|
+
|
|
3
|
+
## 结论与范围
|
|
4
|
+
|
|
5
|
+
审查覆盖本任务插件两次实现提交(04b7294、9725042)、Chat 分页提交(8145eee)及对应合同;以下问题已修复并补回归验证。当前方案在既有 Host / SQLite / Browser Store 内收敛,没有新增框架、依赖、服务或持久化状态机。测试支持这些具体结论,不代表对任意环境作“绝无缺陷”的保证。
|
|
6
|
+
|
|
7
|
+
## 已修复的问题
|
|
8
|
+
|
|
9
|
+
| 级别 | 问题与影响 | 修复与证据 |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| P1 | 两个资料任务占满后,分页生产者等待空位,慢头像拖慢整群名单 | 基础分页独立推进;仅资料队列最多两个在途。member-directory-review 测试在两个任务持续阻塞时仍完成 150 人/3 页 |
|
|
12
|
+
| P1 | 取消、失权、成员变更之后,旧请求或迟到缓存可能回写 | 传播 AbortSignal;失权取消缓存恢复;刷新代次与既有 runtime cache epoch 阻止旧写入。contract/paging/store 测试覆盖取消、失权、重试及晚到结果 |
|
|
13
|
+
| P1 | presentation 被当作名单,可以重新创建已移除成员 | Page 与 Presentation 使用独立类型;Browser/SQLite 共用 applyMemberUpdate。只有 membership 创建实体,资料只能补齐已有实体或明确移除;不增加 tombstone 状态 |
|
|
14
|
+
| P1 | 旧组件的 subscribe 可以激活旧账号,回滚当前数据作用域 | 账号激活仅由运行时生命周期负责;订阅按账号隔离,未激活订阅不请求网络,旧订阅不能影响当前账号 |
|
|
15
|
+
| P1 | 缺少 items 的异常响应被当成空数组,可能删除有效成员 | Host 先验证数组、来源、身份、状态、游标及批次边界,再接受移除事实;错误保留原数据 |
|
|
16
|
+
| P1 | 缓存中的角色被用于管理能力判断 | 删除重复 selfRole/selfStatus 及 fallbackRole;权限只取 group.settings,缓存不授予管理能力 |
|
|
17
|
+
| P2 | 一个成员头像失败导致整批姓名/统计不更新;基础页的零统计被当成真实结果 | 按成员记录资料失败,仅保留不可用头像;已读到的名称与统计正常更新。基础统计显式未知;后端分页独立 DTO 不输出残留统计 |
|
|
18
|
+
| P2 | 旧成员缓存覆盖当前已读详情姓名;账号变化后详情本地状态残留 | 当前已读查询姓名优先,缓存只补缺失项;详情按账号、运行代次、消息标识隔离,退出登录清行 |
|
|
19
|
+
| P2 | 同名成员/未知邀请人按名字合并,混淆不同身份 | invitees 仅按 memberRef 合并;缺少邀请人 ID 时不以名字合并事件;同名测试保留两个人 |
|
|
20
|
+
| P2 | 500 个缓存行立即订阅所有头像,形成无关网络负载 | 复用现有头像 hook,通过 IntersectionObserver 按可见性订阅;500 行/0 可见为 0 请求,8 行可见只加载 8 个 |
|
|
21
|
+
| P2 | 缓存字段和入群事件校验不足,损坏数据可能进入渲染;清理失效引用可能清掉同时修复的缓存 | 成员、事件、人物均按白名单投影和类型校验;精确删除失效引用,不清空并发的新快照 |
|
|
22
|
+
| P2 | 容量查询反复扫描 JSON;隐藏详情的已排队刷新仍可能运行 | 持久化 payload_bytes,容量查询求和小整数;隐藏时清定时器,回调复核前台状态和观察者 |
|
|
23
|
+
| P2 | 新 UI 同时保留分页/全量路径与双构造器,形成迭代过渡态 | 删除新 UI 的全量降级和旧构造器;移除旧完整查询对新缓存的写入。Tools 描述只保留业务用途和参数语义 |
|
|
24
|
+
|
|
25
|
+
## 最终数据边界
|
|
26
|
+
|
|
27
|
+
- Chat 拥有成员事实;分页为实时游标遍历,缺席不是删除。完整遍历后对缺席缓存成员按 ID 核验,只应用明确状态。
|
|
28
|
+
- Host 校验账号/群绑定引用,统一提供分页、资料和缓存。SQLite 是有界展示缓存;缓存不会替代服务端请求,也不决定读权限或写权限。
|
|
29
|
+
- Browser 每个账号/群一份成员 Store,按稳定身份差量合并;组件只订阅和保存交互状态。重复打开抽屉不重复读取名单。
|
|
30
|
+
- 消息参与范围、已读/未读仍来自已读接口,不能从当前成员名单推算。basicOnly 是轻量展示投影;既有完整 API 服务其原有调用方,不是新 UI 回退。
|
|
31
|
+
- 保留必要的取消/代次、短期失败状态、容量上限和一次性 SQLite 结构迁移。没有新增双格式缓存 reader、全量替换、兼容回退或业务状态机。
|
|
32
|
+
|
|
33
|
+
## 验证
|
|
34
|
+
|
|
35
|
+
- 插件全量:431 个文件通过、4 个文件跳过;4672 项通过、5 项跳过。typecheck、build、可执行入口及 Windows 凭据 helper 构建校验通过。
|
|
36
|
+
- 新增/扩展测试覆盖:成员流转、缓存重开、账号/群隔离、失权、并发取消、畸形响应、部分资料失败、可见头像、姓名优先级、同名身份及隐藏详情刷新。
|
|
37
|
+
- Chat:相关 Handler/领域测试、go vet 通过;Mongo 7.0.14 实际验证 500 人/10 页、无重复或跨群,explain 使用原有索引且无 COLLSCAN/SORT。
|
|
38
|
+
- 官方 DSH 0.1.1-rc.2 ToolRuntime:三个新工具真实注册、携带会话上下文执行、卸载通过。仓外 Consumer 从 tgz 安装、公开 SDK 编译及调用通过:缓存 499 人、基础页 49 个活跃成员(另 1 个已退群)、有效后续游标、资料姓名正确。
|
|
39
|
+
- 本机独立 DSH Profile + 真实 Host/SQLite + 合成远端:冷启动 500 人最终为 10 次基础分页、10 次资料请求,无 members/list 回退;打开抽屉不重复刷新。随后远端改名/退群,UI 与 SQLite 同时收敛到 499 人;缓存字节数 387264,与实际 UTF-8 字节数一致。
|
|
40
|
+
- 再次停止并重启 Host、改变端口:缓存请求在 669ms 发出、688ms 返回,740ms 时 499 行可见(相对于页面导航;相对于成员请求约 70ms)。远端分页已于 670ms 发出,但仍在 2 秒模拟延迟中;此时关闭抽屉可正常输入并清空草稿,未发送消息。
|
|
41
|
+
- OpenSpec strict 校验通过。所有源改动位于任务 worktree;没有改动插件版本、依赖、锁文件、用户客户端或生产环境。
|
|
42
|
+
|
|
43
|
+
## 发布与验证边界
|
|
44
|
+
|
|
45
|
+
新成员 UI 只走分页,必须先部署 Chat 分页接口,再接入插件;后端缺少该接口时保留已有缓存并报告请求错误。该后端改动用于真实分页和轻量名单,不是实现本地缓存的前提。
|
|
46
|
+
|
|
47
|
+
本机缺少 Docker,后端 Docker E2E 未运行通过;Mongo 全包有初版交付时已在未修改 master 9f649eac 复现的既有失败。本次新分页的原生 Mongo 等价场景通过。没有宣称后端全量门禁全绿。
|
|
48
|
+
|
|
49
|
+
浏览器验收使用合成业务远端,不能替代正式服务器负载、生产消息发送或 Windows/Linux 实机验收;没有生产延迟保证。未部署、推送、创建 PR 或替换用户安装版本。
|
|
50
|
+
|
|
51
|
+
验收远端 fixture 未实现 Arko 资料、内测群状态和部分会话偏好接口,浏览器对应 502 已按 operation 定位,不属于成员分页/缓存结果;因此未把该隔离场景称为全产品 E2E。最后一轮缓存事件校验修复后,重新打包安装并复验 499 行及改名、正式 Tools 和仓外 SDK。
|
|
52
|
+
|
|
53
|
+
复验期间 official/dev 前进到 6429e25(含发布 0.1.47、输入框样式和交织卡片改动)。本报告全量测试对应本任务原始基线 121f68e 加本任务实现和审查修复,不冒充最新 dev 集成验收;发布前仍需在最终目标上集成并执行相应门禁。
|
package/docs/plugin-release.md
CHANGED
|
@@ -4,9 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
需要手动指定发版级别时,由发版负责人在 GitHub 的 **Actions → 准备插件发版 → Run workflow** 中选择级别并填写一句更新说明。该工作流先更新版本元数据,再安装依赖,执行完整测试、类型检查和构建,验证成功后创建发版 PR,等待合并。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`prepare` 成功后输出精确发布 SHA 和该提交中的版本,随后并行启动两条发布链路:`publish` job 检出该 SHA,完成类型检查、资源校验、构建和 npm 发布,再创建 Git Tag / GitHub Release;Runtime 派发 job 同时向默认分支发送 `arkme-plugin-release-prepared` 事件,由独立的 production workflow 校验 SHA 属于 master 历史、版本与提交一致,检出同一 SHA,构建 `tar.zst`,通过 Backend 获取单对象 STS 上传 OSS,并在 Runtime 校验通过后自动激活。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
生产 Runtime 的构建、上传和激活均不等待 npm 发布、Registry 回读、Tag 或 GitHub Release。npm 发布失败不会阻断 Runtime;Runtime 发布失败也不会阻断 npm。`prepare`(包括版本准备与前置测试)仍是共同前提,失败时两条链路都不会启动;版本准备仍会读取 npm latest。生产 workflow 继续兼容旧的 `arkme-plugin-release-published` 事件,供历史工作流重跑使用。
|
|
10
|
+
|
|
11
|
+
npm 接受发布请求后仍可能异步处理版本。工作流以 15 分钟为等待窗口,每次未通过后间隔 5 秒回读精确版本,并在等待窗口结束时再检查一次;单次请求(含最后一次检查)最多 30 秒。日志会输出 HTTP 状态、网络或 JSON 错误,以及版本、integrity、provenance 的具体缺失或不一致字段。只有全部校验通过后才会继续创建 Tag、GitHub Release 和同步 master 到 dev;这段等待与生产 Runtime 独立。
|
|
12
|
+
|
|
13
|
+
每次普通 PR 发版时,会查询当前 master 版本对应的 `release/v<版本号>` PR 是否已合并。已经合并表示该版本已分配,即使 npm latest 仍落后,新一轮发版也会递增 patch,避免复用可能已经上线的 Runtime 版本。未分配的显式版本递增仍保持原版本;重跑同一次发版时,先复用已有 `release_sha`,并从该提交读取原版本,不重复分配。
|
|
14
|
+
|
|
15
|
+
npm 失败时,Runtime 可以先完成上线,对应的 Git Tag 和 GitHub Release 可能暂未创建。可重跑 npm 所在工作流的失败 job,继续发布同一提交的 npm 包。两条链路的结果分别查看 npm Action 与生产 Runtime Action;派发成功仅表示事件已发出。
|
|
10
16
|
|
|
11
17
|
`pre-release` 分支的每次 push 会走同一套 Runtime 发布链路,但不会发布 npm。测试版本由稳定基准版本的下一补丁与 GitHub run number 组成,例如 `0.1.34` 在 run `128` 中生成 `0.1.35-pre.128`;版本修改只存在于 Action 临时工作区。
|
|
12
18
|
|
|
@@ -16,7 +22,7 @@ npm 接受发布请求后仍可能异步处理版本。工作流以 15 分钟为
|
|
|
16
22
|
|
|
17
23
|
| 发布路径 | Workflow | 默认值 |
|
|
18
24
|
| --- | --- | --- |
|
|
19
|
-
| `master`
|
|
25
|
+
| `master` 发版准备成功后,与 npm 并行发布生产 Runtime | `.github/workflows/publish-production-runtime.yml` | `'false'`:跳过 Runtime 批量测试 |
|
|
20
26
|
| `pre-release` push 后构建测试 Runtime | `.github/workflows/publish-pre-release-runtime.yml` | `'true'`:执行 Runtime 批量测试 |
|
|
21
27
|
|
|
22
28
|
需要调整时,在对应 workflow 中把 `RUN_TESTS` 改为字符串 `'true'` 或 `'false'`。`执行 Runtime 测试` 是独立步骤,仅在值为 `'true'` 时运行 `pnpm test`,测试失败会阻止后续构建和发布;关闭时该步骤显示为跳过。开关只控制批量测试,类型检查、资源校验、构建、打包和发布校验仍然执行。
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# 群成员分页与本地缓存
|
|
2
|
+
|
|
3
|
+
## 数据与生命周期
|
|
4
|
+
|
|
5
|
+
每个环境/账号/群只有一份 Browser 成员 Store。会话、成员抽屉、@ 候选和已读详情订阅它;页面只保存展开、选中和滚动等展示状态。memberRef 是成员身份,sourceKey 是群身份。账号只由运行时生命周期激活,订阅者不能切换全局账号。管理操作能力只取自 group.settings,不能从缓存角色推断。
|
|
6
|
+
|
|
7
|
+
首次订阅时,本地缓存恢复和服务端读取并行开始。缓存命中先显示,但不设置远端新鲜度、不跳过远端请求。第一份远端结果到达后,迟到缓存不能覆盖它。缓存过期、损坏、引用签名失效或读写失败均按缓存未命中处理;授权失败仍明确返回。
|
|
8
|
+
|
|
9
|
+
服务端基础名单每页 50 人、按稳定 user_id 游标读取。每页携带访问校验得到的 selfRole,管理入口不读取缓存中的角色,也不等待自己排到后续页。每页到达就按 memberRef 合并,不清空列表、不替换未变化成员对象。展示补齐最多两个批次并行,每批至多 50 人;基础分页不等待补齐空位。基础页不得抹去缓存中的姓名、头像和统计;展示结果只更新已有成员,不能创建或复活成员。公开资料失败按成员标记,仅保留其旧头像,已获取的姓名、备注和统计照常更新;统计尚未读取时留空并保持行高。跨页同一入群事件按 memberRef 合并 invitees,不按姓名合并身份。
|
|
10
|
+
|
|
11
|
+
分页遍历不是原子快照:某个成员没出现在一页或整个遍历中都不能直接视为退出。遍历结束后,对缺席缓存成员通过现有按用户查询接口明确核验,只应用 removedMemberRefs。移除成员的确认回执直接更新缓存;退出、解散、访问权限失效清掉对应缓存。运行时缓存代次同时隔离请求去重键和缓存写入,防止新读取复用变更前请求。
|
|
12
|
+
|
|
13
|
+
实际 Chat 成员缺失/退群(2001)和会话停用(2002)会同时清除 Browser 与 SQLite 缓存;这些错误只在成员读取合同内归类,不能把一般业务冲突当作删除。
|
|
14
|
+
|
|
15
|
+
成员加入通知 t=24 只触发公共名单回查;退群历史通知 t=27 另外触发历史窗口失效,两者都不携带可直接落地的成员或已读事实。
|
|
16
|
+
|
|
17
|
+
切后台、最后一个订阅者离开、账号或运行实例切换会取消相应旧工作;当前列表在一般网络失败时保留并可重试。权限拒绝还会取消迟到缓存恢复,避免恢复已清除的行。
|
|
18
|
+
|
|
19
|
+
## 接口与发布顺序
|
|
20
|
+
|
|
21
|
+
| 接口 | 合同 |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| Chat POST /api/v1/chats/members/page | chat_session_uid、after_user_id、limit(默认 50,最多 100)、active_only;返回 items、has_more、next_user_id。每页验证当前访问权;复用现有索引;不做统计或外部资料补齐 |
|
|
24
|
+
| source.members.page | Host 将游标封装为账号/群绑定的不透明引用,返回基础成员与入群事件 |
|
|
25
|
+
| source.members.presentation | 接收 1-50 个 memberRef,通过 members/by-user-ids 和公开资料批量接口补齐展示,明确返回已不活跃成员;不扫描整个联系人/会话目录 |
|
|
26
|
+
| source.members.cached | 仅返回当前账号/群的本地展示缓存及 cachedAtMillis,未命中返回 null;不是权限或服务端事实 |
|
|
27
|
+
| source.read-receipts.detail + basicOnly | 先返回服务端已读事实和本地已有展示资料,UI 再订阅成员 Store 更新展示;默认完整查询行为保持兼容 |
|
|
28
|
+
|
|
29
|
+
原有 members/list、source.members、SDK/Tools 默认完整已读查询继续服务其既有调用方。新成员 UI 只有分页路径,没有全量降级或双加载构造器;后端缺少新接口时保留已有缓存并明确报错。因此需先部署 Chat 新分页接口,再接入新插件。basicOnly 是同一已读接口的轻量投影选择,不是版本兼容回退。
|
|
30
|
+
|
|
31
|
+
## 本地缓存
|
|
32
|
+
|
|
33
|
+
复用插件现有 SQLite(环境目录 + user_id + 群 UID),不依赖浏览器端口。缓存格式版本为 1,只接受白名单展示字段;旧格式按未命中处理,不保留双格式读取。表内记录 payload_bytes,容量统计不扫描 JSON 内容。缓存只由 Host 的已授权查询结果写入,不提供 Browser 任意写接口。签名引用按当前账号/群/运行密钥校验;缓存不能授权写操作,所有操作仍经服务端校验。
|
|
34
|
+
|
|
35
|
+
内存最多保留 20 个无人订阅群、合计 20000 名闲置成员。磁盘缓存最多 100 群、合计 32 MiB;单群最多 20000 人/4 MB,14 天过期。超过容量按旧缓存淘汰,不能截断名单伪装完整。活动分页单次遍历最多 200 页,超过时保留已有数据并明确失败。容量与过期都只影响缓存命中,不改变服务端查询结果。
|
|
36
|
+
|
|
37
|
+
## 能力矩阵与验证
|
|
38
|
+
|
|
39
|
+
| 面 | 完成证据 |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| UI | 打包插件安装到新的官方 DSH Profile;真实 Host + SQLite,合成远端数据验证 500 人分页、重启先显、增量修改/移除 |
|
|
42
|
+
| Host | 分页、展示、缓存共用 ChatService / ServiceRuntime / LocalDatabase;游标校验、跨账号/群、失败和缓存代次测试 |
|
|
43
|
+
| Tools | arkme_source_members_page、arkme_source_members_presentation、arkme_source_members_cached;目标 DSH 0.1.1-rc.2 正式 ToolRuntime 注册、带会话上下文调用及卸载测试通过 |
|
|
44
|
+
| SDK | 从不可变 tgz 安装的仓外 Consumer 通过公开 SDK 编译并调用能力探测、缓存、分页、展示;使用 AbortSignal;缺少分页直接返回请求错误 |
|
|
45
|
+
| Client/Harness | 沿用现有 Browser module / Host 载体;无 Electron 或 DSH 源码修改;未发布运行环境或替换用户客户端 |
|
|
46
|
+
|
|
47
|
+
500 个缓存行不会立即触发 500 次头像读取:成员和已读详情行通过 IntersectionObserver 仅在可见时订阅头像。
|
|
48
|
+
|
|
49
|
+
最新逐场景合并前审查见 [member-directory-premerge-review.md](member-directory-premerge-review.md),前轮记录见 [member-directory-review.md](member-directory-review.md)。下列时序是初版交付的历史观察,修复后的复验以该报告为准。
|
|
50
|
+
|
|
51
|
+
## 验证边界
|
|
52
|
+
|
|
53
|
+
在真实 Host/协调器的同负载模拟中,每次上游延迟固定 2 秒:旧 500 人完整读取在冷/暖公开资料缓存下都累计 42 秒;新流程首批 50 人在 2 秒可见、完整资料在 32 秒完成(10 个基础页、10 个成员资料批次、10 个公开资料批次),没有成员触发的全目录扫描。这是 fake-clock 对比,不能作为生产延迟保证。
|
|
54
|
+
|
|
55
|
+
打包插件的本机重启验收中,保留 SQLite、切换到新端口、让远端成员请求延迟 2 秒:缓存接口约 44ms 返回,500 名缓存成员约 89ms 可见,当时远端请求已发出但未返回。远端随后确认一人退出和一处备注修改,页面与 SQLite 均收敛到 499 人。这是单次隔离场景观察,不是跨平台 SLO。
|
|
56
|
+
|
|
57
|
+
Chat 后端新增分页的真实 Mongo 验证覆盖 500 人/10 页、无重复或跨群、索引命中且无 COLLSCAN/SORT;API 鉴权与参数测试、go vet 通过。全量 Go 测试中的 Docker E2E 在本机缺少 Docker;Mongo 全包另有既有失败,已在未修改的 master 9f649eac 上复现,新增分页测试通过。未把这些门禁记为全绿。
|
|
58
|
+
|
|
59
|
+
代码基线:插件 official/dev 121f68e9;后端 master 9f649eac;合同 meta master 8bb0b7a9。没有生产部署、PR、版本变更或 Windows/Linux 实机验收。
|
|
60
|
+
|
|
61
|
+
已存在内容时采用静默刷新:成员标题和已读详情不显示“更新中”或“正在更新”,不插入资料加载行;首次无数据与真实失败保留必要反馈。
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# 侧边栏合并前场景审查
|
|
2
|
+
|
|
3
|
+
审查对象:PR #381 的 `codex/c20260908-sidebar-local-first`。起点为 6eb6943,修复提交 e2e2b19;与目标 dev 5581c82 合并后,再完成缓存 Bot 引用修复;最终运行代码为 c0ac896。客户端读取官方 master 02f86d2,仅用于隔离验收,没有客户端源码改动。
|
|
4
|
+
|
|
5
|
+
## 结论与证据口径
|
|
6
|
+
|
|
7
|
+
本轮不是仅检查 diff 或依赖原有绿灯:先复现失败,再修复,再运行相关回归及完整套件。下表覆盖本对话已明确的场景,以及本次改动直接改变的读取、持久化、实时投影与正常业务操作链。测试通过只证明所列断言,不等同于所有平台、所有服务状态或任意数据规模都已经实际验收。
|
|
8
|
+
|
|
9
|
+
后端合同核对了 Chat 后端 的 `gin/api/chat_list.go`、`internal/chat/models.go` 中的 ChatUnreadSnapshot、成员更新/解散入口,以及插件既有 ConversationListPreferenceService。后续按用户要求固定这个基线,不再追踪 dev 的继续推进。没有更改后端接口或复制后端权限判断。
|
|
10
|
+
|
|
11
|
+
## 已确认问题与修复
|
|
12
|
+
|
|
13
|
+
| 问题 | 原因与处理 |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| Bot 隐藏状态重启后丢失 | Bot visibility 放在 metadata;源会话的后续空增量覆盖了它。改为按 Bot 引用合并,明确删除再清除。 |
|
|
16
|
+
| 已读被旧目录响应恢复 | 只比较消息序号无法表示已读进度。使用后端 read_seq,接收 read-ack,持久化到对应源摘要;不使用 unread 数量取最小值猜测已读。 |
|
|
17
|
+
| 切账号覆盖待写增量 | 单个待写槽被新账号覆盖。改为按账号合并,单写入者串行冲刷,最多保留 8 个待写账号。 |
|
|
18
|
+
| 同步期间偏好变更丢失 | 扫描中直接忽略失效事件。合并为一次后续扫描,保留原 owner 和取消边界。 |
|
|
19
|
+
| 新连接拿到旧通知基线 | raw baseline 已结束而后续工作未结束时仍复用结果。后续连接重新建立基线;图片预热不进入通知等待链。 |
|
|
20
|
+
| 陈旧可见性引用残留 | 查询期间 sourceRef 已更新仍接纳旧引用。只接纳当前源引用及未被本地更新覆盖的结果。 |
|
|
21
|
+
| 头像清除语义混用 | 稀疏实时字段与权威空头像走相同合并。分离装饰确认与实时 upsert,群头像、头像数组都可正确清除。 |
|
|
22
|
+
| Bot 头像未持久化 | 原来只缓存 profile image。Bot image 的网络读取使用自己的引用验证;历史字节只按当前账号缓存键读取,普通消息图片保持原逻辑。 |
|
|
23
|
+
| 磁盘故障导致头像不可用 | 成功下载后,派生缓存失败又抛出整个读取。缓存故障保留可用图片及最后成功缓存。 |
|
|
24
|
+
| 不必要的头像 SQL 扫描 | 未到磁盘预算也扫描全部引用以选择淘汰项。预算以内直接完成,超限时才查询,并保护 Bot 元信息中的头像引用。 |
|
|
25
|
+
| 新 Bot 不出现在侧边栏 | 有 Host projection 后直接跳过所有可见性查询。补查 Host 尚未覆盖的新条目,稀疏 Bot 快照增量合并。 |
|
|
26
|
+
| Bot 置顶迁移与即时操作 | 旧 Browser 置顶会被 Host 空数组覆盖,新 Bot 尚未进入 Host 列表时置顶失败。逐项迁移,写成功后消费旧记录;缺少条目时从 Bot owner 确认。 |
|
|
27
|
+
| 移除操作提前隐藏 | 临时反馈集合参与最终条目过滤。移除成功前保留条目,错误后继续可操作。 |
|
|
28
|
+
| 退出/解散群后残留条目 | 原来依赖列表全量替换;增量合并后该机制失效。成功回执产生独立 removedSourceKeys,贯穿 Host、Browser、SQLite;迟到响应不复活,权威重新入群结果可恢复。 |
|
|
29
|
+
| 缓存 Bot 引用跨重启失效 | 原 Bot v2 引用是当前进程注册表句柄,不能直接作为持久操作目标。恢复为独立的、账号绑定的目录查询引用,调用时从 Bot 服务重新确认当前目标;原 v2 的过期/注销规则保持。 |
|
|
30
|
+
| SDK 修改 Host 内存 | 返回值共享缓存对象。读取和事件边界复制快照。 |
|
|
31
|
+
| 取消、焦点和截断不明确 | 调用者取消只脱离等待;虚拟分块保留焦点并提供无 IntersectionObserver 回退;Tool 截断显式标记并给出枚举方式。 |
|
|
32
|
+
|
|
33
|
+
## 场景到实现与验证的映射
|
|
34
|
+
|
|
35
|
+
下表中的测试路径均位于 tests/;Host 为 services/conversation-directory-service.ts,DB 为 local-database.ts,Store 为 client/chat-directory-store.ts,Navigation 为 client/ArkmeVirtualWorkspace.tsx。均指当前插件仓 src/ 下的真实实现。
|
|
36
|
+
|
|
37
|
+
| 场景 | 入口到数据结果 | 验证依据 |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| 无缓存首批显示 | sources.list(localFirst) → Host.read → firstPage → Store.applyHostPage → Navigation | services/conversation-directory-service.test.ts:阻塞第二页、头像、写盘仍返回20条;客户端阻塞/放行响应验收 |
|
|
40
|
+
| 首次与后台无 loading | Navigation 直接显示已到达条目,失败/重试独立 | arkme-virtual-workspace.test.tsx;真实桌面20帧静默检查 |
|
|
41
|
+
| 暖缓存完整目录 | Host.activate → DB.readDirectoryCache → snapshot | local-database.test.ts;Host 完整缓存恢复测试;桌面92条/6 Bot读取 |
|
|
42
|
+
| 头像、置顶、隐藏重启恢复 | 账号行、元信息、图片字节分别恢复 | local-database.test.ts;前次真实离线证据保留,本轮新增 Bot 隐藏增量回归 |
|
|
43
|
+
| 20条自动遍历超过10页 | Host.startScan 固定页大小并自动游标循环 | Host 测试240条/12页 |
|
|
44
|
+
| 缺席保留 | 稳定源键增量合并,不按页缺席删除 | Host 缓存缺席及 Bot 缺席测试 |
|
|
45
|
+
| 重复/无效游标 | 扫描失败并保留已成功页面 | Host 重复游标测试 |
|
|
46
|
+
| 后续页失败与重试 | phase=failed;保留已展示数据;原重试入口 | Host 后台失败测试、Navigation 失败入口测试 |
|
|
47
|
+
| 实时数据早于缓存响应 | Store 按 revision 处理迟到快照,补缺项但不覆盖新项 | chat-directory-store.test.ts |
|
|
48
|
+
| 旧消息与新置顶同时返回 | latestSequence 与 policy.update_at 分别比较 | Host merge 测试、chat-directory-store.test.ts 的置顶版本测试 |
|
|
49
|
+
| 已读发生在列表返回前 | Host.pendingReadAcks → 按源身份应用 | Host 首屏前 read-ack 测试 |
|
|
50
|
+
| 已读后同水位旧刷新 | readSequence 单调保护,继续持久化 | Host read-ack 后刷新测试 |
|
|
51
|
+
| 恢复旧消息后未读增加 | read_seq 未推进时允许同消息序号下计数增加 | Host restored unread relations 测试;后端 ChatUnreadSnapshot 合同 |
|
|
52
|
+
| 免打扰与普通未读 | projectArkmeChatAttentionFromMuted;保留原通知 owner | chat-directory-store.test.ts、services/chat-realtime-service.test.ts |
|
|
53
|
+
| @我、消息撤回/重编辑 | 原 Timeline owner 与通知/已读投影保持分离 | conversation-send-directory.test.tsx、chat-directory-store.test.ts、services/chat-realtime-service.test.ts |
|
|
54
|
+
| 扫描中隐藏/找回 | 偏好版本保护 + 单次补偿扫描 | Host 失效事件测试、conversation-directory-visibility-lifecycle.test.ts |
|
|
55
|
+
| 引用更新期间查询返回 | currentRefs 和 mutation revision 过滤 | Host 旧/新 sourceRef 查询乱序测试 |
|
|
56
|
+
| 移除成功/失败 | 偏好命令回执 → Host确认 → UI更新;失败仍可重试 | chat-directory-pin-ui.test.tsx 的成功/失败交互 |
|
|
57
|
+
| 联系人找回会话 | 原 ConversationDirectoryVisibilityService.restoreSource/restoreBotConversation | conversation-directory-visibility-lifecycle.test.ts |
|
|
58
|
+
| 退出群/解散群 | GroupService 成功回执 → forgetSource → removedSourceKeys → DB删除/Store移除 | services/group-service.test.ts + Host、DB、Store 显式移除回归 |
|
|
59
|
+
| 退出后的迟到页面/实时数据 | 旧扫描 revision 不能覆盖移除;实时数据需要权威目录复核 | Host、Store 群移除测试 |
|
|
60
|
+
| 再次入群 | 后续权威列表返回该身份,解除移除并恢复行 | Host、Store authoritative rejoin 测试 |
|
|
61
|
+
| 新 Bot 打开即展示 | 新条目补查可见性;不因 Host 快照暂缺而消失 | chat-directory-pin-ui.test.tsx 的 New Bot 真实组件测试 |
|
|
62
|
+
| 新 Bot 立即置顶 | pinBot 缺项从 Bot owner 确认,再持久化 | Host newly created Bot 测试 |
|
|
63
|
+
| Bot 隐藏增量持久化 | DB Bot visibility 按引用合并 | local-database.test.ts 的 source-only delta + reopen |
|
|
64
|
+
| 旧版 Bot 置顶升级 | migrateBotDirectoryPreferences 逐项确认/消费 | bot-directory-preferences.test.ts:部分失败、重试、不重复迁移 |
|
|
65
|
+
| Bot 明确删除 | removedBotRefs;清除对应本地置顶/隐藏,不用快照缺席猜测 | Host forgetBot + bot-chat-directory-projection.test.ts |
|
|
66
|
+
| Chat Bot 与独立 Bot | 仅同 opaque chatSourceKey 的 Chat Bot 投影普通会话未读;独立 Bot 保留 Record 归属 | bot-chat-directory-projection.test.ts、arkme-bot-service.test.ts |
|
|
67
|
+
| Bot 活动与预览新旧顺序 | 独立比较 Bot 消息时间,保留较新内容,不混用源序号 | bot-chat-directory-projection.test.ts、Host.rememberBots |
|
|
68
|
+
| 普通 Bot 列表不启动侧边栏 | rememberBots 仅处理已激活的目录 owner | arkme-bot-service.test.ts 的精确请求序列回归 |
|
|
69
|
+
| Arko/发给自己不串账号 | 原入口取数据后按 captured userId 投影 | Host late special/visibility account 测试;arkme-persistent-sidebar.test.ts |
|
|
70
|
+
| 账号切换时旧页返回 | owner generation + AbortController | Host reset/旧账号页测试 |
|
|
71
|
+
| 切账号时仍有待写数据 | 按账号 coalesce,保存原 userId 后串行写入 | Host delayed disk + switchUser 回归 |
|
|
72
|
+
| 单个读者取消 | 只移除自己的等待,其他读者和后台扫描继续 | Host aborted caller/shared first page 测试 |
|
|
73
|
+
| 本地置顶写失败 | 回滚本地投影并返回明确错误 | Host disk-full pin 测试 |
|
|
74
|
+
| 新旧通知连接 | 原 ChatRealtimeService 校验连接/账号代次;已完成基线不供后续连接复用 | services/chat-realtime-service.test.ts;Host later connection 测试 |
|
|
75
|
+
| 头像下载、落盘失败 | 媒体 owner 保留最后成功字节,缓存失败不破坏下载结果 | services/media-service.test.ts 的 cache read/write fault |
|
|
76
|
+
| Bot 离线头像及账号授权 | 当前账号 DB 行可读取历史字节;网络读取仍验证进程引用,不复用过期上游 URL | services/media-service.test.ts 的离线、异账号测试 |
|
|
77
|
+
| Bot 缓存引用重启后使用 | BotService.restoreDirectoryBots → 账号签名的目录查询引用 → openDirectoryBotRef → 当前 Bot list → 当前 target | services/bot-service.test.ts:重启、旧句柄仍过期、目标从 Subject 变为 Chat、篡改/异账号/已删除拒绝 |
|
|
78
|
+
| 稀疏头像与权威移除 | 仅 hydration 确认清除;消息增量不代表删除 | Host sparse avatar / removed group avatar;Store Host-delta 清除 |
|
|
79
|
+
| SDK 快照可变性 | 受控输出复制 | Host consumer mutation 测试 |
|
|
80
|
+
| SDK/Tools 边界 | provider capability、公开 SDK、原 grant/正式 ToolRuntime | tools/directory-runtime.test.ts、tools/registrar.test.ts;桌面真实 Host 调用 |
|
|
81
|
+
| Tool 有界结果 | truncated 与普通游标枚举提示 | tools/directory-runtime.test.ts:80条缓存只输出20条 |
|
|
82
|
+
| 大列表与键盘焦点 | 20行分块、相邻窗口、选中/聚焦保活;无 IO 时完整可达 | arkme-directory-window.test.tsx |
|
|
83
|
+
| 普通收发、富媒体、群操作 | 业务命令 owner 不变;派生缓存错误不改变已成功命令结果 | conversation-send-directory.test.tsx、services/group-service.test.ts、完整测试套件 |
|
|
84
|
+
|
|
85
|
+
## 复杂度与边界
|
|
86
|
+
|
|
87
|
+
未新增依赖、定时轮询器、第二个目录扫描 owner 或独立同步状态机。保留的状态分别表达账号代次、扫描/首屏完成、已确认读游标、偏好 mutation、明确成员移除和待写增量;这些事实不能合并成一个“更新版本”。新增成员移除字段是成功业务回执的投影,不是靠分页缺席推导的删除。
|
|
88
|
+
|
|
89
|
+
目录仍有20,000行/32 MiB持久化预算,头像256 MiB、单图8 MiB;待写账号最多8个,超限显式拒绝缓存排队,本地置顶不得伪装保存成功。以上是保护上界,不表示已完成上界账号的长期压力验收。测试未连接生产账号执行发消息、退群、解散或修改服务端隐藏/置顶;对应写场景通过真实实现和受控服务边界测试验证。Windows/Linux 本轮未实机运行,不声明跨平台零回归。
|
|
90
|
+
|
|
91
|
+
## 最终验证记录
|
|
92
|
+
|
|
93
|
+
- 固定审查基线:5581c826f01f39467a7cd2f6e6e6bc54ac00b348;未继续追随 dev 推进。本报告映射 45 个场景。
|
|
94
|
+
- 最终完整套件:433 个测试文件通过、4 个文件跳过;4786 项通过、6 项跳过。另有客户端同版本官方 Session ToolRuntime 对真实 Host 的验收测试通过1项。
|
|
95
|
+
- typecheck、构建、Windows helper 产物静态校验、pack、diff-check 通过;版本、依赖与根 README 相对固定基线没有本任务修改。
|
|
96
|
+
- 不可变验收制品:sidebar-review-8ffd7652c6c49ec5.tgz,12,030,891 字节,SHA-256 `8ffd7652c6c49ec5919227a614cfa315c6c121a233f6660d6186c75c4b2c169c`。运行时代码未包含本机源码路径。
|
|
97
|
+
- macOS 隔离客户端:首次目录响应恢复92条源会话、6个 Bot,6个 Bot 均使用缓存目录查询引用;完成服务端同步后仍为92条/6 Bot。
|
|
98
|
+
- 仓外 Consumer 通过公开 SDK 入口完成目录读取及本地 Bot 置顶/恢复往返;缓存 Bot 引用通过真实 Host 成功打开当前 Bot 会话(该目标返回0条消息)。
|
|
99
|
+
- 使用客户端同版本的官方 DSH Session ToolRuntime 0.1.1-rc.2 发现并执行目录 Tool:缓存92条、输出20条、明确标记截断。
|
|
100
|
+
- 实际页面:首次请求被阻塞时0条会话且无 loading/骨架;放行后展示,连续20帧后台同步无加载提示。
|
|
101
|
+
- 制品在带客户端完整同版本 Harness 模块的隔离 Profile 验证。首次裸 Profile 缺少 Host peer 的启动失败与补齐日志保留;正式验收采用完整 Host 装配。
|
|
102
|
+
- 冷暖耗时沿用前次报告,仅作为历史结果;本轮没有将历史数字当作重新测量的数据。
|
|
103
|
+
|
|
104
|
+
已复现的问题均已修复;当前分支所列场景中未发现剩余的已确认业务阻断项。合并判断以本报告的场景、版本与验证环境为界,Windows/Linux 实机及上界规模的长期压力测试仍未完成。
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# 会话目录本地恢复与增量同步
|
|
2
|
+
|
|
3
|
+
初始实现基线:插件 official/dev 121f68e;客户端 origin/master 02f86d2。参考 前端 pre-release 6157a309c 的本地快照、首屏提交、后台自动补齐和独立加载状态。
|
|
4
|
+
|
|
5
|
+
## 行为合同
|
|
6
|
+
|
|
7
|
+
当前账号先恢复 SQLite 目录和本地头像,再校准服务端。无缓存时第一页立即发布,不等待头像下载、缓存写盘或第二页。每页固定 20 条,自动遍历至 hasMore=false;每页只按稳定身份增量合并,缺席不删除。缓存、远端首屏、完整扫描分别记录;失败不把部分结果标成完整。旧账号结果、旧消息水位和旧置顶版本不得回退新状态。
|
|
8
|
+
|
|
9
|
+
Host 统一拥有目录扫描和持久化,通知基线复用同一轮取数但仍校验连接代次。隐藏状态由既有权限和偏好 owner 确认后投影;本地头像保存图片字节,下载失败保留旧图片。Browser 只订阅目录变化并展示。
|
|
10
|
+
|
|
11
|
+
## 能力矩阵
|
|
12
|
+
|
|
13
|
+
| 面 | 接入 | 验证要求 |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| UI | 现有 sources.list 的 localFirst 模式与现有 SSE | 冷/暖首屏、分页、头像、置顶和隐藏恢复 |
|
|
16
|
+
| SDK | listSources(root, localFirst)、setBotDirectoryPin 类型安全选项与能力声明 | 仓外 consumer 编译及真实 Host 调用 |
|
|
17
|
+
| Tools | arkme_sources_list 的 local_first 选项及 arkme_bot_conversation_pin | 注册、grant 和真实 Harness 会话调用 |
|
|
18
|
+
| Host | 一个目录 owner、SQLite 增量写入与媒体缓存 | 并发、取消、账号、陈旧结果、失败和重启测试 |
|
|
19
|
+
| Client | 现有独立 App Data 与本地插件装配 | 隔离 macOS Electron 场景和制品安装验证 |
|
|
20
|
+
|
|
21
|
+
## 验收工作负载
|
|
22
|
+
|
|
23
|
+
记录插件和客户端 SHA、运行环境、数据规模、每页数量。区分应用启动、目录请求到第一条可点击、首屏头像完成、全目录完成。相同数据与网络条件分别测试无目录/头像缓存、有完整持久缓存和重启后离线恢复;阻塞第二页、头像和持久化验证冷首屏不被阻塞。真实环境与受控网络数据分别报告,不将模拟耗时当线上实测。
|
|
24
|
+
|
|
25
|
+
完整目录仅保存摘要,媒体由既有下载并发限制和明确磁盘容量管理;同步游标循环、账号切换和超限必须显式失败。浏览器可视窗口限制 DOM,数据完整性不受窗口大小影响。
|
|
26
|
+
|
|
27
|
+
## 实施状态
|
|
28
|
+
|
|
29
|
+
已实现并完成 macOS 隔离客户端验收。全量测试、实际账号冷/暖加载、业务网络不可用恢复、SDK Consumer、官方 Session ToolRuntime 和制品安装证据见本任务交付报告;生产渠道交付独立于本地验收,不随此功能自动发布。
|
|
30
|
+
|
|
31
|
+
## 资源和兼容边界
|
|
32
|
+
|
|
33
|
+
- SQLite 目录最多 20,000 条/32 MiB;达到预算会显式失败,不把截断结果当完整列表。
|
|
34
|
+
- 头像沿用单图 8 MiB 限制,磁盘缓存预算 256 MiB;已被目录引用的图片受保护,容量不足保留旧图并报告缓存失败。
|
|
35
|
+
- Browser 只保留附近的20行分块,已选中的分块保持可用;不活跃头像最多保留256项,等待队列最多512项。
|
|
36
|
+
- 页面恢复和本地 Bot 置顶通过 `sources.list(localFirst)`、`conversation.directory.bot-pin` 与公开 SDK/Tools 消费;写操作保留明确用户授权。
|
|
37
|
+
- 缓存是派生数据;操作权限继续由现有服务校验。分页缺席不作为删除证据。
|
|
38
|
+
|
|
39
|
+
## 后台同步展示
|
|
40
|
+
|
|
41
|
+
首次无缓存、增量补齐和后台刷新均不显示加载横条、转圈、骨架或同步文案;首批数据到达后直接展示。失败提示和重试入口按既有规则保留。同步状态继续由 Host 持有,展示隐藏不改变分页、持久化或失败状态语义。
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## 合并前场景复核修正
|
|
45
|
+
|
|
46
|
+
- 已读采用后端 `unread_snapshot.read_seq`;消息序号、已读游标与 policy.update_at 分别比较,不以同一消息水位下未读数只减不增作假设。
|
|
47
|
+
- 同步中的目录偏好失效合并为一次后续扫描;一个消费者取消只退出等待,账号切换取消整个 owner。已完成的通知基线不能供后续连接复用。
|
|
48
|
+
- Bot 缓存缺席不删除,明确删除独立投影;新建、联系人打开和普通 Bot 查询保持原业务能力。旧 Browser 置顶逐项迁入 Host,写入成功才消费旧记录。
|
|
49
|
+
- Bot 头像同样持久化;缓存读写故障不覆盖已成功下载的图片。实时事件的稀疏头像字段与权威头像删除分别处理。
|
|
50
|
+
- 磁盘缓存故障保留最后成功快照并记录诊断;本地 Bot 置顶必须持久化成功。远端扫描完成不等于磁盘写入成功,二者不得混作一个成功状态。
|
|
51
|
+
- Tool 限制输出时显式标记 truncated 并提示使用普通游标目录,不把输出截断伪装成完整枚举。
|
|
52
|
+
|
|
53
|
+
退出/解散群的成功回执通过独立的 removedSourceKeys 投影移除缓存行;旧分页、装饰与实时增量不得复活该条目。后续权威目录确认重新入群时才恢复。分页缺席仍不作为成员变更证据。
|
|
54
|
+
|
|
55
|
+
缓存 Bot 条目使用独立的、账号绑定的目录查询引用,操作时重新向 Bot owner 确认目标;不延长原进程 Bot 句柄或过期图片 URL 的授权。历史 Bot 头像只允许按当前账号缓存键读取本地字节。
|
|
56
|
+
|
|
57
|
+
## 侧边栏切换
|
|
58
|
+
|
|
59
|
+
对话与联系人目录按账号分别延迟挂载,首次访问后切换只隐藏,不重新挂载或重新请求会话目录。隐藏时暂停联系人后续自动翻页和 Arko 预览轮询,关闭浮层,后台共享目录更新不得抢走当前导航。虚拟分块区分整面板隐藏与实际滚动离屏,隐藏时保留已显示行;显示前恢复原滚动位置。账号切换仍销毁旧账号组件状态。
|
|
60
|
+
|
|
61
|
+
已有本地或服务端目录、Bot、Arko 或发给自己的数据时,不显示全局会话加载失败及重试占位;完全没有可用数据才保留失败重试入口。Host 同步失败状态继续保留,不改变真实同步结果。
|
package/lib/bundle-runtime.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as invokeArkmeBundle, n as arkmeBundleActive, r as deactivateArkmeBundle, t as applyArkmeBundleHostExtension } from "./bundle-runtime-
|
|
1
|
+
import { i as invokeArkmeBundle, n as arkmeBundleActive, r as deactivateArkmeBundle, t as applyArkmeBundleHostExtension } from "./bundle-runtime-CE73h3vS.js";
|
|
2
2
|
export { applyArkmeBundleHostExtension, arkmeBundleActive, deactivateArkmeBundle, invokeArkmeBundle };
|