跳到主要内容

Issue 通知与人工任务 CI 回归

从其他入口打开 Issue 的已读同步​

Issue 活动可见后,通过既有 POST /v1/loop-items/{id}/read 接口, 在同一事务中更新活动游标和当前用户关联通知的已读状态。 关联依据是通知 payload 的 itemId 或 issueId;其他 Issue、其他用户 及无 Issue 关联的通知保持不变,已读通知的时间戳也保持不变。 即使 Issue 内容此前已读,再次可见时仍确认已读,以消费随后到达的通知。 读取失败时保留未读计数;成功响应后立即刷新通知中心。

现有 CI checkpoint collaboration-issue-comment-notification 同时运行原导航场景 和独立的 issue-notification-read 场景。新增场景通过真实 Backend 建立三个 已读 Issue 和未读通知,依次从系统通知、深链、看板卡片打开,验证通知持久化 及应用内计数从 3 降到 0,无需手动刷新,且其他 Issue 的通知在打开前保持未读。 场景使用隔离 Electron 与数据库,由共享 runner 清理环境。 每次 Issue 深链沿用现有 focusRequest 传递新的打开请求;即使复用已打开的 抽屉,也会重新确认通知已读,同时保留活动滚动进度。场景最后在抽屉保持打开时 发送一条后续通知,再从系统通知打开同一 Issue,验证计数再次归零。

聚焦验证还覆盖重复读取、当前用户隔离、无关联通知、失败不刷新,以及已读内容 再次进入的情况。活动尚未加载或不可见时,不提前确认已读。

2026-10-27:隔离 Electron 的 issue-notification-read checkpoint 全部通过, 覆盖上述三个入口和同一抽屉的后续通知。Backend 的 4 条已读 API 回归通过, 通知中心及 API 37 条、活动及路由 146 条、共享滚动 19 条、checkpoint 契约 4 条通过。 测试夹具保留项目 ID 的字符串表示,避免大整数转换为 JavaScript Number 后丢失精度。

同轮 CI 的共享桌面产物构建等待了不存在的 Rust 镜像:消费端计算摘要时没有包含 生产端的 backend-rs/examples/**。桌面 CI 和缓存预热统一使用生产端的源码输入, 分类契约测试检查三处输入一致,避免通过扩大等待时间掩盖镜像标识不一致。

故障依据与修复边界​

PR #3763 的提交 f124f3d2e 在桌面 Core 分片 12、15 失败:

  • project-assignment-notification 的失败快照包含绑定 Task 的入口, 但入口位于视口下方;可见元素点击命令不负责滚动。 场景必须检查展开状态、等待任务行、滚动到入口后再点击,不能删除模型断言。
  • collaboration-issue-comment-notification 首次深链高亮成功, 重载后点击同一条通知不再高亮。同一地址没有产生新的定位请求, 评论组件又按评论 ID 永久去重。

每次打开评论通知生成内部 focusRequest,由现有项目路由传到评论组件。 匹配已有页签时忽略此请求标识,避免重复页签。每个请求只滚动、高亮一次; 后台消息更新不重复高亮。快速再次点击时先结束旧动画,再在下一帧启动新动画。 不增加用户填写字段,不改变模型、设备路由或人工提交权限。

QA 计划​

短暂高亮的观测窗口​

PR #3947 的 Core 分片 8 在评论落点等待完成后读取到 animation-name=none。 原场景依次等待详情与评论各稳定 400 毫秒,再读取持续 2 秒的动画;忙碌的 runner 可能在发出样式查询前耗尽动画窗口。场景现在复用现有渲染帧采样,在导航前记录 精确评论选择器的 animationName,落点稳定后检查记录,不延长动画或等待预算。 每次导航先排除上一次高亮,保留深链和收件箱各自的帧证据;没有目标动画、其他 评论动画、导航失败和超过原 8 秒高亮预算仍然失败。原 6 秒落点预算、重载、 链接打开和已读持久化检查不变,不修改产品高亮行为。

聚焦测试覆盖动画结束后的记录保留、精确评论归属及失败清理。真实 Electron 仍运行现有 collaboration-issue-comment-notification checkpoint,由 Core CI 调用。

本地门禁资源隔离​

低内存主机可设置 WEWORK_PRE_PUSH_RENDERER_SHARD_CONCURRENCY=1,串行执行 原来的 1/2、2/2 两个完整 renderer 分片;默认仍并发两个分片。 WEWORK_PRE_PUSH_TEST_WORKERS 仍控制每个分片的 worker 数。串行模式保留所有 文件、原有超时和失败传播,首个分片失败也会执行第二个;无效并发值明确阻止推送。 门禁回归检查分片不重叠、覆盖完整及失败不被吞掉。

可见活动游标单测先等评论加载,再用受控动画帧驱动真实滚动读取 hook,避免依赖 宿主及时调度 requestAnimationFrame。帧执行前不得读,执行后仍必须只确认可见的 序号 2,不得确认屏外序号 3,并保留父级回调检查;不修改产品滚动或展示逻辑。

环境:隔离的 Electron、真实 Backend/Socket.IO/设备,只有模型服务使用场景内的测试服务。 本地使用聚焦 Vitest 验证,完整桌面验收由现有 GitHub CI Core 分片执行; 不操作个人 WeWork 窗口,不通过重跑、扩大超时或跳过断言掩盖失败。

前置条件操作预期
人工 Issue 已绑定远程 AI 草稿 Task改变新会话默认模型,展开并滚动到已有 Task,再打开使用绑定模型,不继承新会话默认值;原 Issue 仍由人工提交
评论通知首次打开深链定位目标评论在原有时间预算内滚动并高亮,其他评论不高亮
重载后仍打开同一评论,旧动画已结束从通知中心再次点击原通知复用页签,产生新请求,重新高亮并保存已读状态
高亮尚未结束快速再次定位同一评论重启动画,不延续旧动画的剩余时间
定位请求没有变化后台消息刷新或普通重渲染不重复滚动和高亮
评论尚未加载,或已离开评论加载目标评论;离开后再返回加载后定位;返回可以再次定位
定位动画已排队卸载组件清理动画帧和定时器

现有 CI 场景保留真实远程设备、任务绑定、交付、人工提交/验收、已读持久化断言。 失败时保留界面快照和运行日志,成功时检查截图。 场景使用各自独立的用户、项目和运行目录,交由桌面 runner 的清理流程回收。

验证记录​

2026-10-09:修复前新增的重复通知路由及评论高亮断言均失败,修复后均通过。 聚焦测试 7 个文件、177 条用例通过,覆盖通知路由、评论定位、Issue 编辑器和人工任务抽屉。 ESLint、Prettier、TypeScript 和两个场景的语法检查通过。 本地结果不代表真实远程设备或桌面 E2E 已通过;最终结论以修复提交的 CI 为准。

推送后 main 新增 Issue 归档功能,导致“更多信息”菜单冲突。 合并保留归档文案、回调和点击外部关闭弹层的行为;补充单测验证两者同时可用。 归档相关的共享组件、桌面编辑器和 Backend 接口用例需在合并后重新验证。

main 随后合入统一 Issue 执行状态与回复逻辑。合并必须同时保留智能体 Team/ 模型绑定和执行线程、工作目录、Issue 会话上下文,不能因没有选择模型而丢失上下文。 聚焦测试发现续跑成功后的状态推送漏传 to_view 所需的数据库参数,已修正; 验证原会话续跑、忙碌任务排队、重新指派后回复和人工绑定的智能体模型不变。 抽屉重开单测直接建立活动项目,复用交互设备,明确断言旧面板已关闭, 保留迟到的任务地址不能覆盖新面板的断言,不扩大用例超时。

同一处理人取消后重新指派​

提交 f5bbaab0c 的 Core 分片 12 在人工验收后重新指派阶段失败: human_work.state 仍为 accepted,场景要求新一轮指派为 none。 旧版 PATCH 接口只清空负责人字段,没有撤销活动流里的指派事件, 同一处理人的后续指派因此被错误去重。取消直接人工指派时复用现有 指派事件撤销服务,与负责人字段修改保持在同一版本校验事务内。

回归覆盖待验收、已完成 Issue 重新指派给原处理人或其他处理人; 新指派使用新事件 ID,保留 Issue 原状态,显式接手后才进入进行中。 未取消时重复指派仍幂等;版本冲突不撤销指派;工作流步骤的指派不被误删。 不删除已有验收记录,不改变桌面 E2E 断言或超时。

2026-10-10 主分支合并回归​

保留 main 的 Issue 标题/描述行内编辑、附件预览及个人任务工作区选择, 同时保留人工处理、AI 草稿、标签编辑和点击外部关闭属性弹层。 普通个人任务不继承智能体的项目执行环境;人工 AI 辅助继续使用最新保存的 项目设备/目录和人工指派绑定。评论通知统一使用内部 focusRequest 参数。 时间转换复用可配置的数据库时区,SQLite 及已有时区的时间不被重复偏移。

创建项目/切换视图与成员、机器人、小组指派的组件测试拆为独立场景, 每个场景建立自身前置条件并保留原断言,不扩大超时或添加重试。 合并后检查共享组件、桌面编辑器/抽屉/通知、Web 详情和数据库时间回归; 聚焦单测和类型检查不代表真实远程设备或桌面 E2E 验收。

执行期间编辑 Issue 的版本冲突​

PR #3869 的 Core 分片 9 在 collaboration-local-agent-dispatch 中失败: 返回 Issue 后标题仍是旧值。运行日志显示,标题保存的 todos.update 返回 version_conflict,后台执行状态同时推进了 Issue 版本;界面中的草稿值不代表 保存成功。

详情保存仅提交用户修改的字段。出现版本冲突时读取最新 Issue,确认这些字段 没有被其他编辑修改后,使用最新版本提交一次;同字段冲突、其他请求错误和 再次发生的版本冲突都保持失败。执行状态、优先级等未编辑字段不会被旧草稿覆盖。 只改处理人时不再先写入未变化的详情字段,直接使用当前版本执行分配。

聚焦回归覆盖执行状态变化与标题编辑并发、同字段冲突、网络错误、再次冲突、 空修改和处理人分配。现有桌面 E2E 的保存及返回标题断言保持不变,最终以修复 提交的 CI 结果验证真实集成。

协作成员首次活动的 MySQL 竞争​

成员的运行事件可以先于活动记录到达。查找尚不存在的运行记录时,不应对 Runtime 二级索引执行 FOR UPDATE:InnoDB 的间隙锁可能与其他成员的首次插入冲突。先查找已有记录的主键,再对该主键执行锁定并刷新内容,既避免锁住未创建的成员,也保留已有活动的串行更新。

backend/tests/integration/runtime_activity_mysql.py 在真实 MySQL 的 REPEATABLE READ 下用同步屏障复现独立成员首次插入,并验证同一活动的并发更新不会丢失结果。平台 E2E 的第一个分片调用该验证;原有双轮协作 E2E 继续检查两个成员并行运行、完整成果及新经理任务。