[ISSUE-0072] cleanupOrphanScrapeWindows 误杀用户新标签页¶
agent code review 找到的 🔴 严重 #2,比 ISSUE-0070 修法引入的回归更严重。
病灶¶
v0.10.101 的 cleanupOrphanScrapeWindows 第一版:
function looksLikeSharedWindow(w: any): boolean {
// ❌ ±80 太宽 — 680-840 × 480-640 全算"我们的"
return Math.abs(w.width - 760) < 80 && Math.abs(w.height - 560) < 80;
}
// ❌ tabs 全是 about:blank 或 chrome://newtab 就关
const isOrphan = tabs.length <= 2 &&
tabs.every(t => /^(about:blank|chrome:\/\/newtab)/i.test(t.url || ''));
用户场景:浏览器启动时¶
用户开 Chrome → 自动恢复几个窗口(含一个新标签页 — 默认尺寸约 720×500)
↓
SW 启动(晚 2-3s)→ cleanupOrphanScrapeWindows
↓
扫所有窗口 → 720×500 落进 680-840×480-640 ✓ + 全 chrome://newtab ✓
↓
判定"孤儿" → tabs.remove → **用户的新标签页被强关**
严重性¶
- 用户没操作的情况下窗口消失 = 严重 UX 事故
- Cent Browser + RDC 环境下 windows.get 不稳定 → cleanupOrphan 可能被频繁触发
- 用户根本不知道是扩展干的 → 可能怪 Chrome / Cent Browser
修(v0.10.103)¶
1. 尺寸阈值 ±80 → ±20¶
function looksLikeSharedWindow(w: any): boolean {
return Math.abs(w.width - 760) < 20 && Math.abs(w.height - 560) < 20;
}
760×560 是我们自己 windows.create 写死的,chrome 不会自己改尺寸,±20 足够覆盖渲染舍入误差。
2. 只关 about:blank(不关 chrome://newtab/)¶
const isOrphan = tabs.length > 0 && tabs.every(t => {
const u = String(t.url || '');
return u === '' || u === 'about:blank' || /^about:blank\??/i.test(u);
});
about:blank是我们windows.create({ url: 'about:blank' })的占位 — 安全关chrome://newtab/是用户主动开的新标签页 — 不关
3. 双层防护:尺寸 + URL¶
两个条件 AND 关系,单独命中其中一个不关。
元教训¶
- 改 bug 引入更严重 bug 是经典反模式。v0.10.101 修孤儿 → 引入误杀 → 修法 1 周内才被 agent code review 发现
- CLAUDE.md "禁止阉割式修复"原则反向:cleanup 类操作要最保守(误关比累积危险得多)
- 新功能加 cleanup 类时应该写多个测试场景:
- 用户自己开的新标签页
- 用户调小过的浏览器窗口
- 浏览器恢复 session 时
- agent code review 在 v0.10.86-102 找到 10 项问题(4 真需修)。每个大变更后都应该跑一次
audit_grep 防退化¶
audit_grep:
- pattern: "looksLikeSharedWindow[\\s\\S]{0,300}< 80"
description: "阈值 ±80 是误杀风险,必须 ±20"
- pattern: "chrome:\\/\\/newtab.*remove"
description: "不允许关 chrome://newtab/ tab"
相关¶
- [[0070-shared-window-orphan-storage-race-sw-kill|0070-共享窗口孤儿累积-storage漏写-SW-kill竞态]] — 引入此 bug 的修复
- 禁止阉割式修复 — 反例:cleanup 类反而要"宁可不清也别误杀"