Skip to content

fix: Merge into existing deliverable tab group instead of creating duplicates#9

Merged
iFurySt merged 2 commits into
iFurySt:mainfrom
n1k0ver3E:fix/merge-deliverable-tab-groups
May 11, 2026
Merged

fix: Merge into existing deliverable tab group instead of creating duplicates#9
iFurySt merged 2 commits into
iFurySt:mainfrom
n1k0ver3E:fix/merge-deliverable-tab-groups

Conversation

@n1k0ver3E
Copy link
Copy Markdown

@n1k0ver3E n1k0ver3E commented May 11, 2026

变更摘要

修复 OBU Chrome 扩展中 moveToDeliverables 创建重复 deliverable 标签分组的 bug。当 Chrome 会话恢复带回一个 "✅ Open Browser Use" 分组、或者扩展存储的 deliverableGroupId 与实际窗口状态出现偏差时,后续每次 finalize_tabs(deliverable) 都会新建一个同名分组,导致标签栏出现多个标题相同但 id 不同的独立分组。本次改动让扩展按 (windowId, title) 查找已有分组,合并任何重复后再加入新 tab,保证每个 Chrome 窗口至多只有一个 ✅ Open Browser Use 分组。

改了什么?

  • apps/chrome-extension/background.js — 重写 BrowserBackend.moveToDeliverables
    • windowId 拆分传入的 tab id(顺便修了一个潜在的跨窗口 bug:chrome.tabs.group({groupId, tabIds}) 要求 group 与 tab 同窗口)。
    • 对每个窗口调用 chrome.tabGroups.query({windowId, title: DELIVERABLE_GROUP_TITLE}) 找出所有同名分组。
    • 取 id 最小的那个作为主分组保留(若 SessionStore.deliverableGroupId 在结果中则优先选它),把其他同名分组里的 tab 合并到主分组。
    • 新 deliverable tab 加入主分组;只有窗口里完全没有同名分组时才新建。
    • 把主分组 id 持久化为新的 deliverableGroupId
  • apps/chrome-extension/move-to-deliverables.test.mjs(新增)— Node vm 沙箱单元测试,加载 background.js + 伪 chrome.* 接口,覆盖:复用已有分组、合并重复分组、无分组时新建、多窗口隔离,共 4 个场景全部通过。
  • docs/ARCHITECTURE.md — 在 deliverable 分组段落补一句"每个 Chrome 窗口至多存在一个 ✅ Open Browser Use 分组,moveToDeliverables 按 windowId + title 查询并合并重复",让架构文档反映真实不变量。
  • docs/histories/2026-05/20260511-1439-deliverable-group-merge-duplicates-fix.md(新增)— 按 docs/HISTORY_GUIDE.md 约定补的 history 条目,记录用户原始诉求、改动 scope、设计动机、验证方式与受影响文件,方便后续 agent 继续这条线时拿到完整上下文。

为什么现在做?

  • 用户已经在真实 Chrome 中遇到 bug:会话恢复后再 finalize_tabs,标签栏出现两个标题相同的 "✅ Open Browser Use" 分组。
  • Chrome 的 tabGroups API 按数字 id 识别分组、不按标题,扩展原本完全没有按标题查询已有分组的逻辑,等于这个 bug 是"必现,只是触发条件依赖会话恢复时机",不会自愈。
  • OBU 的 JSON-RPC 不暴露 tabGroups.query / tabGroups.move,上层 agent 无法绕过扩展自行合并,只能在扩展内修
  • 修法聚焦:只动一个函数、加一份单元测试、补两份文档,影响面小、易于复核,越早合入越能避免后续 finalize 把分组叠加得更乱。

验证情况

  • make ci — 仓库无 make ci 目标;实际跑的是 node apps/chrome-extension/move-to-deliverables.test.mjs,4/4 通过。
  • 已完成相关测试或手工验证
    • 单元测试:复用 / 合并重复 / 新建 / 多窗口隔离 4 个场景全部通过。
    • 真机端到端验证:在用户实际 Chrome 中复现 bug 状态(两个独立的绿色 Open Browser Use 胶囊),加载修复后的 background.js 并 reload 扩展,对一个新 example.com 标签调用一次 finalize_tabs(deliverable),截图确认两个重复胶囊自动合并为单一胶囊,内含全部 3 个标签(两个 x.com + Example Domain)。
  • 如果行为变化,文档已经同步更新 — docs/ARCHITECTURE.md 已补"每个窗口至多一个 deliverable 分组"的不变量描述。
  • 如果涉及代码或流程变更,history 已补齐或更新 — 已新增 docs/histories/2026-05/20260511-1439-deliverable-group-merge-duplicates-fix.md
  • 如果用户可感知,release notes 已更新 — 暂未更新 docs/releases/feature-release-notes.md,按仓库惯例释放新 patch 版本时再附加一行 "Fix: deliverable tab groups no longer duplicate after session restore",并随版本号一起入。

关联上下文

  • Execution plan:N/A(点对点 bug 修复,无独立执行计划)
  • Product spec:N/A
  • History entrydocs/histories/2026-05/20260511-1439-deliverable-group-merge-duplicates-fix.md
  • 后续技术债
    • BrowserBackend.ensureSessionGroup 存在完全一致的 bug 模式(只读存储的 chromeGroupId,不按 title 查询),会话恢复后也可能产生重复的 session task 分组,建议另起 PR 用相同的"按 windowId + title 查询并合并"思路修。
    • 可以考虑把 tabGroups.query 暴露到 JSON-RPC 层,让上层 agent 也能做合并/校验,避免再次出现"看得到但管不了"的死角。
    • SessionStore.deliverableGroupId 是单一全局字段,多窗口场景下仅记录最后一次写入的 id;要做到彻底干净可改成 Map<windowId, groupId> 持久化,目前靠 title 查询已能自愈,所以列为低优先级。

Pn1ko and others added 2 commits May 11, 2026 12:36
moveToDeliverables previously trusted a single stored deliverableGroupId
and minted a fresh tab group whenever the stored id didn't resolve. When
Chrome restored a session that already had a "✅ Open Browser Use" group,
the stored id and the restored group diverged, so each finalize_tabs call
created a parallel group with the same title — the user ended up with
multiple visually identical deliverable groups that Chrome's tabGroups
API cannot merge by title.

The new implementation groups incoming tabs by windowId and, for each
window, queries chrome.tabGroups for an existing group titled
DELIVERABLE_GROUP_TITLE. Any duplicates are collapsed into the
lowest-id surviving group; new tabs are added to that group; a fresh
group is created only when none exists. Per-window handling also fixes
the latent bug where chrome.tabs.group({groupId, tabIds}) was called
with tabs in a different window than the stored group.

Adds a Node test suite (move-to-deliverables.test.mjs) that loads
background.js in a vm sandbox with a fake chrome.* surface and covers
four scenarios: reuse-existing, merge-duplicates, create-when-none, and
per-window isolation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the history entry expected by docs/HISTORY_GUIDE.md so future agents
can find the rationale, and clarifies the per-window merge-by-title
invariant in ARCHITECTURE.md so the deliverable-group section reflects
the new reconciliation behavior rather than just "id is persisted".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@n1k0ver3E n1k0ver3E changed the title Merge into existing deliverable tab group instead of creating duplicates fix: Merge into existing deliverable tab group instead of creating duplicates May 11, 2026
@iFurySt iFurySt merged commit 02182a8 into iFurySt:main May 11, 2026
2 of 3 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.

3 participants