AT-T022..T026:浏览器适配器最外层 bare catch 把一切失败坍缩成 browser_execution_unconfirmed_no_replay——实测「Chromium 没装」与「工具被拒」不可区分
- Dominant language
- TypeScript
- Stars
- 0
- Forks
- 0
- Avg merge
- 1h 7m
- Merged PRs (30d)
- 969
Description
> 来自 #3005(AT 验收 B4+B5,BROWSER 组)。实测 SHA `94f6dda037ca94da73d1b0a6359763a54db832a3`。
## 结论
`PlaywrightMcpBrowserAdapter.invoke` 的最外层是一个**不接收异常对象**的 `catch`,把所有失败重新抛成同一句 `browser_execution_unconfirmed_no_replay`,且**不挂 `cause`**。后果是验收方案 §11 明令要区分的那条——「中断浏览器 transport,错误必须与零结果区分」——在这一层无法成立:**环境缺失(BLOCKED)与契约违反(FAIL)返回同一个字符串**。
这与 #3005 记录的 PR #2981 是同一形状的问题(`except Exception: … from None` 把陈旧引用被拒 / 5xx / 超时 / 响应超限 / schema 违规坍缩成同一句话),只是 #2981 修的是别处,**适配器最外层这一处还在**。
## 证据(实测,非推断)
### ① 代码
`apps/api/src/infrastructure/agent-run/playwright-mcp-browser-adapter.ts:527-534`:
```ts
} catch {
if (signal.aborted) void this.receipts.markUnconfirmed(...).catch(() => undefined);
else await abortable(this.receipts.markUnconfirmed(...), signal).catch(() => undefined);
void this.release(context.bindingId).catch(() => undefined);
throw new Error('browser_execution_unconfirmed_no_replay');
}
```
`catch {` —— 连异常变量都没绑,原因**在语法层面就被丢弃了**。
### ② 实测:真实 lane 报的就是这一句,看不出为什么
```
cd apps/api && WORKSPACEX_REAL_BROWSER=1 pnpm exec vitest run \
--config vitest.browser-adapter.config.ts tests/agent-runtime/playwright-mcp-browser-real.test.ts
× W10 real Playwright MCP and Chromium acceptance > navigates, snapshots, fills, clicks,
isolates storage, and writes a real PNG 40ms
→ browser_execution_unconfirmed_no_replay
× W10 … > renders desktop/mobile workspace previews with actual CSP blocking network requests
→ browser_execution_unconfirmed_no_replay
Test Files 1 failed (1) Tests 2 failed | 4 skipped (6)
```
40ms 就红、且两条报同一句话。按 #3005 判据纪律第 1 条(红 ≠ 跑过)这**看起来**像「没跑到」,但**没有任何输出能证实**——这正是问题本身。
### ③ 临时打桩才看得见真因(已还原,仓库 clean)
把那句改成 `catch (e) { console.error('cause:', e); … }` 后重跑,真因立刻出现:
```
[AT-B5 DIAG] browser adapter cause: browserType.launch: Executable doesn't exist at
/opt/pw-browsers/chromium_headless_shell-1243/chrome-headless-shell-linux64/chrome-headless-shell
╔════════════════════════════════════════════════════════════╗
║ Looks like Playwright was just installed or updated. ║
║ Please run the following command to download new browsers: ║
║ pnpm exec playwright install ║
╚════════════════════════════════════════════════════════════╝
```
即:本机 `/opt/pw-browsers` 只有 `chromium-1194` / `chromium_headless_shell-1194`,而 `playwright@1.63.0-alpha-2026-08-31` 要 **1243**。这是**环境前置缺失**,判 BLOCKED——但要打一次桩才知道,验收方在没有源码修改权限的场景下根本判不出来。
## 影响
- AT-T022…AT-T026 五项在缺 Chromium 的环境里只能记 **BLOCKED**,且**判据必须靠打桩**,不可复现给 reviewer。
- 生产上同样:用户/运维看到的只有一句 `browser_execution_unconfirmed_no_replay`,分不清「浏览器运行时挂了」「页面拒绝了这次点击」「响应超限」。
## 建议修法(未自行动手;很小,但改的是失败语义,不自作主张)
1. `catch {` → `catch (cause) {`,重抛时挂上 `{ cause }`:`throw new Error('browser_execution_unconfirmed_no_replay', { cause })`。**对外契约字符串不变**(现有断言不受影响),只是链上多了原因。
2. 落 receipt 时把原因分类写进 `markUnconfirmed` 的诊断字段(至少区分 `session_launch_failed` / `upstream_tool_error` / `timeout` / `oversize` / `stale_ref`),使 §11 的「transport 中断 ≠ 零结果」在数据里可判,而不只是在日志里。
3. 补一条测试:注入一个必然失败的 session factory,断言 receipt 上的分类**不是** `upstream_tool_error`——否则这条分类同样会退化成恒真。
## 附带(premise 更正,供 #3005 协调者)
#3005 正文说「preflight 的检查是 `real_model_require_vars WORKSPACEX_BROWSER_MCP_ENDPOINT`,而那个变量恰恰由 workflow 自己硬编码,是恒真门控」。**这条在本 SHA 上已经不成立**:`.harness/scripts/vm/s013-real-model-evidence.sh:53-56` 里那句存在性检查旁边就写着它测不了什么,真正的门是紧接着的 `browser_ready | tee`——一个最多重试 30 次、每次真的跑 `browser-mcp-probe.mjs` 握手的检查(#2930 加的)。脚本头是 `set -euo pipefail`,所以 `| tee` **不会**吞掉 `browser_ready` 的退出码,这道门是能红的。
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in apps/api/src/infrastructure/agent-run/playwright-mcp-browser-adapter.ts:527-534 and read how invoke records unconfirmed receipts and releases the binding. Run the real-browser test at tests/agent-runtime/playwright-mcp-browser-real.test.ts, then add coverage using a failing session factory. Done means the external error text remains compatible while its cause and receipt diagnostics distinguish environment or transport failures from valid zero results.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- playwright, typescript
- Domain
- backend, testing-qa
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100