diff --git a/docs/development/release-preparation-0.15.0.md b/docs/development/release-preparation-0.15.0.md
index 920d6cb9..7f41a4df 100644
--- a/docs/development/release-preparation-0.15.0.md
+++ b/docs/development/release-preparation-0.15.0.md
@@ -169,6 +169,22 @@ Agent 产品源码及 Runtime 锁未因这四项改变,Agent
最终提交前再次核对源码、测试、依赖和发布资源与该已验证 tree 一致,
仅追加本段验证结果;完整候选仍需自己的主分支 CI 与生产构建通过。
+### 完整候选后的焦点同步修正
+
+`5cdffee` 的 Agent [CI 36343184917](https://github.com/mesalogo/goodbuddy/actions/runs/36343184917)
+三个原生构建和源码校验通过;桌面
+[CI 36343184916](https://github.com/mesalogo/goodbuddy/actions/runs/36343184916)
+为 5,369 通过、51 跳过、2 失败,生产构建未执行。
+会话采集的焦点等待可被 `body` 后代文字误满足,菜单随后被真正的消息聚焦关闭;
+另一个原生 Runtime 控件用例在启动完成前打开失焦关闭的菜单。
+本轮收紧消息焦点的元素断言,并等待项目能力请求及启动渲染后再打开 Runtime 选项。
+不重试菜单、不跳过场景、不改产品实现。
+
+完整冻结 tree `2ddc0da69b6bcb42d8272c44dd11e3e6904d0438` 在 Host 全量
+**5,371 通过、51 跳过、0 失败**,耗时 748.21 秒。
+Windows 原生 Runtime 聚焦用例、类型检查和 lint 通过。
+提交仅追加本段记录;仍须等待修正候选的主分支 CI 和生产构建。
+
## 数据、兼容与验收边界
Desktop schema 48 新增魔法笔记会话来源元数据。升级前应备份完整用户数据,
diff --git a/docs/quality/recurring-defects.md b/docs/quality/recurring-defects.md
index d4642fe9..a0a60376 100644
--- a/docs/quality/recurring-defects.md
+++ b/docs/quality/recurring-defects.md
@@ -53,6 +53,10 @@ AI 列 280。测试同步原生输入与实际状态后独立复跑通过,保
键盘阶段才显示;失败信息应包含实际焦点、输入框禁用/可见状态及保存状态。
取消边界测试不使用 `setTimeout(0)` 猜测异步 I/O 的完成次序。Continue 启动前取消
用例改在既有启动环境回调处触发中止,仍断言未启动子进程及临时配置清理。
+2026-09-28 完整候选 CI 又暴露两处同类等待缺陷:采集用例以
+`activeElement.textContent` 包含消息文字判断导航完成,`body` 也会满足,随后真正的
+消息聚焦会关闭刚打开的菜单。应同时断言焦点元素是消息 `article`,不只匹配后代文本。
+原生 Runtime 控件用例须等当前项目的能力请求及启动渲染完成,再打开失焦关闭的选项菜单。
## 整窗遮罩与原生拖动区域
diff --git a/src/renderer/src/App.test.tsx b/src/renderer/src/App.test.tsx
index 517d08f1..ea37b17d 100644
--- a/src/renderer/src/App.test.tsx
+++ b/src/renderer/src/App.test.tsx
@@ -9256,6 +9256,11 @@ describe("App", () => {
render();
+ await waitFor(() => expect(api.runtimeCustomization.getNativeSnapshot).toHaveBeenCalledWith(
+ expect.objectContaining({ provider: "opencode", projectId }),
+ ));
+ // Let startup selection and its focus effects finish before opening these options.
+ await act(async () => { await new Promise((resolve) => requestAnimationFrame(() => resolve())); });
openComposerOptions();
const agentPicker = await screen.findByRole("button", {
name: /OpenCode Runtime Agent/u,
diff --git a/tests/support/magic-notes-capture-main.ts b/tests/support/magic-notes-capture-main.ts
index 32807469..c643735d 100644
--- a/tests/support/magic-notes-capture-main.ts
+++ b/tests/support/magic-notes-capture-main.ts
@@ -110,7 +110,7 @@ app.whenReady().then(async () => {
await wait(`Boolean(document.getElementById('magic-note-entry-${captured.id}'))`, 'full workspace exact entry')
evidence.workspaceOpened = true
await click('View source message', `document.getElementById('magic-note-entry-${captured.id}')`)
- await wait('document.activeElement?.textContent?.includes("CAPTURE_HISTORY_501_END")', 'source message focus')
+ await wait('document.activeElement?.matches("article.message") && document.activeElement.textContent.includes("CAPTURE_HISTORY_501_END")', 'source message focus')
evidence.sourceOpened = true
await click('Conversation actions')
await click('Save conversation to note')