From a9630ef327c9b8bb8ae3c8be196d463a88985cbe Mon Sep 17 00:00:00 2001 From: "lusendong.6789" Date: Thu, 12 Nov 2026 22:52:39 +0800 Subject: [PATCH 1/3] fix: require real backend execution in Dashboard source acceptance Signed-off-by: lusendong.6789 Co-authored-by: TRAE CLI --- examples/personal-workspace-browser-smoke.mjs | 8 +++ .../first-goal-home-live.mjs | 9 +-- .../runtime-readiness-live.mjs | 6 +- .../scenario-context.mjs | 7 ++- .../personal-workspace-browser/shard.test.mjs | 62 +++++++++++++++++++ 5 files changed, 84 insertions(+), 8 deletions(-) diff --git a/examples/personal-workspace-browser-smoke.mjs b/examples/personal-workspace-browser-smoke.mjs index 33b295f1a8..4f7cd18e04 100644 --- a/examples/personal-workspace-browser-smoke.mjs +++ b/examples/personal-workspace-browser-smoke.mjs @@ -172,6 +172,12 @@ scenarioCatalog.push(completedHistoryIsolationScenario); scenarioCatalog.push(agentModelSelectionScenario); export { scenarioCatalog }; +export function requireBackendReadback(scenario, result) { + if (scenario.requiresBackendReadback && result.backendReadbackVerified !== true) { + throw new Error(`Required backend readback did not execute: ${scenario.id}`); + } +} + async function main() { const requestedScenario = process.env.LOOPX_PERSONAL_WORKSPACE_SCENARIO; const scenarios = requestedScenario @@ -207,10 +213,12 @@ async function main() { ? browser : { newPage: (options = {}) => browser.newPage({ locale: "zh-CN", ...options }) }; const result = await scenario.run({ browser: scenarioBrowser, collectCoverage, url }); + requireBackendReadback(scenario, result); coverageEntries.push(...result.coverageEntries); results[scenario.id] = { status: "PASS", note: result.note, + ...(scenario.requiresBackendReadback ? {backend_readback_verified: true} : {}), duration_ms: Date.now() - startedAt, }; } catch (error) { diff --git a/examples/personal-workspace-browser/first-goal-home-live.mjs b/examples/personal-workspace-browser/first-goal-home-live.mjs index be511050f2..75f0e20f99 100644 --- a/examples/personal-workspace-browser/first-goal-home-live.mjs +++ b/examples/personal-workspace-browser/first-goal-home-live.mjs @@ -6,7 +6,7 @@ import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { createInterface } from "node:readline"; import { resolveTestPython } from "../../scripts/test-python.mjs"; -import { outputDir, packaged, repoRoot } from "./fixture.mjs"; +import { outputDir, repoRoot } from "./fixture.mjs"; import { fetchWorkspaceBackend, openWorkspacePage } from "./scenario-context.mjs"; // End to end against the real service: a disposable `loopx chat` on a fresh @@ -23,9 +23,10 @@ import { fetchWorkspaceBackend, openWorkspacePage } from "./scenario-context.mjs export const firstGoalHomeLiveScenario = { id: "first-goal-home-live", + requiresBackendReadback: true, async run({ browser, collectCoverage, url }) { - // The real service serves the packaged bundle; the development lane has none to serve. - if (!packaged) return { coverageEntries: [], note: "live first-Goal home runs against the packaged bundle only" }; + // Both lanes prepare the verified Chat bundle for the real backend. The + // source lane keeps Vite's source maps while proxying this same owner. const root = await mkdtemp(join(tmpdir(), "loopx-first-goal-live-")); const python = resolveTestPython({ repoRoot }); const child = spawn(python, ["-u", resolve(repoRoot, "examples/personal-workspace-browser/fresh-install-backend.py"), root], { cwd: repoRoot, env: { ...process.env, PYTHONPATH: repoRoot }, stdio: ["ignore", "pipe", "pipe"] }); @@ -130,7 +131,7 @@ export const firstGoalHomeLiveScenario = { await page.locator(".personal-goal-row", { hasText: title }).first().waitFor({ state: "visible", timeout: 20_000 }); await firstGoal.waitFor({ state: "hidden", timeout: 20_000 }); await page.screenshot({ path: resolve(outputDir, "first-goal-home-live-home.png"), animations: "disabled" }); - return { coverageEntries: await session.close(), note: "real loopx chat on a fresh runtime: zero-Goal discovery with its own warning; first Goal registered from the page through the real action owner with the stub Agent's failed first Turn reported honestly; registry written, directory lists it" }; + return { coverageEntries: await session.close(), backendReadbackVerified: true, note: "real loopx chat on a fresh runtime: zero-Goal discovery with its own warning; first Goal registered from the page through the real action owner with the stub Agent's failed first Turn reported honestly; registry written, directory lists it" }; } catch (error) { if (diagnostics) process.stderr.write(diagnostics); await session?.close().catch(() => {}); diff --git a/examples/personal-workspace-browser/runtime-readiness-live.mjs b/examples/personal-workspace-browser/runtime-readiness-live.mjs index 122af36ea4..84adf6ec92 100644 --- a/examples/personal-workspace-browser/runtime-readiness-live.mjs +++ b/examples/personal-workspace-browser/runtime-readiness-live.mjs @@ -7,7 +7,7 @@ import { join, resolve } from "node:path"; import { createInterface } from "node:readline"; import { waitForHttp } from "../dashboard-browser-smoke-support.mjs"; import { resolveTestPython } from "../../scripts/test-python.mjs"; -import { outputDir, packaged, repoRoot } from "./fixture.mjs"; +import { outputDir, repoRoot } from "./fixture.mjs"; import { fetchWorkspaceBackend, openWorkspacePage } from "./scenario-context.mjs"; // End to end against the real service: a disposable `loopx chat` whose Codex, @@ -19,8 +19,8 @@ import { fetchWorkspaceBackend, openWorkspacePage } from "./scenario-context.mjs export const runtimeReadinessLiveScenario = { id: "runtime-readiness-live", + requiresBackendReadback: true, async run({ browser, collectCoverage, url }) { - if (!packaged) return { coverageEntries: [], note: "live runtime readiness runs against the packaged bundle only" }; const root = await mkdtemp(join(tmpdir(), "loopx-readiness-live-")); const python = resolveTestPython({ repoRoot }); const child = spawn(python, ["-u", resolve(repoRoot, "examples/personal-workspace-browser/runtime-readiness-backend.py"), root, "--with-goal"], { cwd: repoRoot, env: { ...process.env, PYTHONPATH: repoRoot }, stdio: ["ignore", "pipe", "pipe"] }); @@ -152,7 +152,7 @@ export const runtimeReadinessLiveScenario = { const codex = catalog.adapters.find((row) => row.agent_id === "codex"); assert.equal(codex.available, true, "The real catalog now reports Codex available"); assert.equal(codex.unavailable_reason, undefined); - return { coverageEntries: await session.close(), note: "real loopx chat service, live PATH probe, real re-check" }; + return { coverageEntries: await session.close(), backendReadbackVerified: true, note: "real loopx chat service, live PATH probe, real re-check" }; } catch (error) { await session?.page.screenshot({ path: resolve(outputDir, "runtime-readiness-live-failure.png"), animations: "disabled" }).catch(() => {}); if (session?.errors.length) process.stderr.write(`${JSON.stringify(session.errors)}\n`); diff --git a/examples/personal-workspace-browser/scenario-context.mjs b/examples/personal-workspace-browser/scenario-context.mjs index 0f49f75046..8a275c546c 100644 --- a/examples/personal-workspace-browser/scenario-context.mjs +++ b/examples/personal-workspace-browser/scenario-context.mjs @@ -14,8 +14,13 @@ export async function fetchWorkspaceBackend(route, backendUrl, { pathname } = {} try { return await route.fetch({ url: target.href, headers }); } catch (error) { + // Playwright preserves the native socket code in its first error line. + // Only that transport signature (or a typed errno), never response prose, + // permits the existing bounded read retry. Writes remain single-attempt. + const socketCode = error?.code ?? /^route\.fetch: (?:connect|read|write) (ECONNRESET|ECONNREFUSED|EPIPE)(?:\s|$)/u + .exec(String(error?.message ?? error).split("\n", 1)[0])?.[1]; const retryable = ["GET", "HEAD"].includes(request.method()) - && /\b(?:ECONNRESET|ECONNREFUSED)\b/u.test(String(error?.message ?? error)) + && ["ECONNRESET", "ECONNREFUSED", "EPIPE"].includes(socketCode) && attempt < 2; if (!retryable) throw error; await new Promise(resolve => setTimeout(resolve, 50 * (attempt + 1))); diff --git a/examples/personal-workspace-browser/shard.test.mjs b/examples/personal-workspace-browser/shard.test.mjs index 2f0c179c41..4df9f5f5e7 100644 --- a/examples/personal-workspace-browser/shard.test.mjs +++ b/examples/personal-workspace-browser/shard.test.mjs @@ -1,6 +1,8 @@ import assert from "node:assert/strict"; import {test} from "node:test"; import {selectScenarioShard} from "./shard.mjs"; +import {requireBackendReadback} from "../personal-workspace-browser-smoke.mjs"; +import {fetchWorkspaceBackend} from "./scenario-context.mjs"; test("shards cover each current and newly appended scenario exactly once", () => { for (const length of [49, 50, 51]) { const catalog = Array.from({length}, (_, id) => ({id})); @@ -16,3 +18,63 @@ test("invalid partitions fail before running a partial acceptance", () => { assert.throws(() => selectScenarioShard([1,2,3], shard)); } }); + +test("a required backend journey cannot qualify from an early return", () => { + const required = {id: "backend-fixture", requiresBackendReadback: true}; + for (const result of [{coverageEntries: []}, {backendReadbackVerified: false}]) { + assert.throws(() => requireBackendReadback(required, result), /Required backend readback did not execute/); + } + assert.doesNotThrow(() => requireBackendReadback(required, {backendReadbackVerified: true})); + assert.doesNotThrow(() => requireBackendReadback({id: "ordinary-fixture"}, {coverageEntries: []})); +}); + +function proxyFixture(method, failures) { + const calls = []; + const response = {ok: true}; + return {calls, response, route: { + request: () => ({method: () => method, url: () => "http://127.0.0.1:5196/api/chat/sessions?goal_id=fixture", + headers: () => ({host: "127.0.0.1:5196", origin: "http://127.0.0.1:5196"})}), + fetch: async options => { + calls.push(options); + if (failures.length) throw failures.shift(); + return response; + }, + }}; +} + +test("native socket failures retain bounded GET and HEAD recovery", async () => { + for (const method of ["GET", "HEAD"]) { + for (const code of ["ECONNRESET", "ECONNREFUSED", "EPIPE"]) { + const fixture = proxyFixture(method, [Object.assign(new Error("native socket failure"), {code})]); + assert.equal(await fetchWorkspaceBackend(fixture.route, "http://127.0.0.1:9123"), fixture.response); + assert.equal(fixture.calls.length, 2); + assert.deepEqual(fixture.calls[0], fixture.calls[1]); + assert.equal(fixture.calls[0].url, "http://127.0.0.1:9123/api/chat/sessions?goal_id=fixture"); + assert.equal(fixture.calls[0].headers.origin, "http://127.0.0.1:9123"); + } + } + const fixture = proxyFixture("GET", [new Error("route.fetch: write EPIPE\nCall log:\nrequest")]); + assert.equal(await fetchWorkspaceBackend(fixture.route, "http://127.0.0.1:9123"), fixture.response); + assert.equal(fixture.calls.length, 2); +}); + +test("persistent socket failure cannot exceed the existing three attempts", async () => { + const errors = Array.from({length: 3}, () => Object.assign(new Error("socket failure"), {code: "EPIPE"})); + const last = errors[2]; + const fixture = proxyFixture("GET", errors); + await assert.rejects(fetchWorkspaceBackend(fixture.route, "http://127.0.0.1:9123"), error => error === last); + assert.equal(fixture.calls.length, 3); +}); + +test("write requests and arbitrary diagnostic prose never authorize a retry", async () => { + for (const method of ["POST", "PUT", "PATCH", "DELETE"]) { + const error = Object.assign(new Error("socket failure"), {code: "EPIPE"}); + const fixture = proxyFixture(method, [error]); + await assert.rejects(fetchWorkspaceBackend(fixture.route, "http://127.0.0.1:9123"), observed => observed === error); + assert.equal(fixture.calls.length, 1); + } + const error = new Error("Backend result mentioned EPIPE; this is not a socket signature"); + const fixture = proxyFixture("GET", [error]); + await assert.rejects(fetchWorkspaceBackend(fixture.route, "http://127.0.0.1:9123"), observed => observed === error); + assert.equal(fixture.calls.length, 1); +}); From eedfc5e2416dcba0b8d431aac0b1a6682beea059 Mon Sep 17 00:00:00 2001 From: "lusendong.6789" Date: Thu, 12 Nov 2026 23:09:35 +0800 Subject: [PATCH 2/3] test: bind research scope help dismissal to its keyboard trigger Signed-off-by: lusendong.6789 Co-authored-by: TRAE CLI --- .../research-record-search.mjs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/personal-workspace-browser/research-record-search.mjs b/examples/personal-workspace-browser/research-record-search.mjs index 8346ebbd2e..30a2492b79 100644 --- a/examples/personal-workspace-browser/research-record-search.mjs +++ b/examples/personal-workspace-browser/research-record-search.mjs @@ -8,9 +8,19 @@ import {outputDir, repoRoot} from "./fixture.mjs"; import {startHistoryOwner} from "./research-history.mjs"; async function readEmptyScope(page, results, message) { - await results.getByRole('button',{name:'No matches in loaded records',exact:true}).focus(); + const search = results.getByRole('searchbox'); + const query = await search.inputValue(); + const trigger = results.getByRole('button',{name:'No matches in loaded records',exact:true}); + await trigger.focus(); + // Enter opens the help even when the trigger retained focus after an earlier + // dismissal; focusing an already focused element does not fire onFocus. + await trigger.press('Enter'); await page.getByRole('tooltip').getByText(message,{exact:true}).waitFor(); - await page.keyboard.press('Escape'); + // Bind dismissal to the current trigger after rendering. A global Escape + // can reach the search input and clear the filter instead of only the help. + await trigger.press('Escape'); + await page.getByRole('tooltip').waitFor({state:'hidden'}); + assert.equal(await search.inputValue(), query, 'Dismissing scope help preserves the selected query'); } export const researchRecordSearchScenario = { From 5f046061b3fbc54e33275b4c545899e5653ff440 Mon Sep 17 00:00:00 2001 From: "lusendong.6789" Date: Thu, 12 Nov 2026 23:56:18 +0800 Subject: [PATCH 3/3] docs: qualify complete Dashboard source execution against the retained RFC gates Signed-off-by: lusendong.6789 Co-authored-by: TRAE CLI --- .../rfcs/goal-workspace-surface-v0.md | 31 ++++++++++++++ .../rfcs/goal-workspace-surface-v0.zh-CN.md | 24 +++++++++++ .../rfcs/monorepo-distribution-split-v0.md | 41 +++++++++++++++++++ .../monorepo-distribution-split-v0.zh-CN.md | 30 ++++++++++++++ 4 files changed, 126 insertions(+) diff --git a/docs/architecture/rfcs/goal-workspace-surface-v0.md b/docs/architecture/rfcs/goal-workspace-surface-v0.md index 10aee26bb2..4ab1290832 100644 --- a/docs/architecture/rfcs/goal-workspace-surface-v0.md +++ b/docs/architecture/rfcs/goal-workspace-surface-v0.md @@ -720,6 +720,37 @@ the existing expansion budget. This qualifies the browser driver's target selection; product focus recovery and the causes of earlier partial inventories retain their separate evidence requirements. +**Checkpoint 2026-11-12 (source-coverage execution).** +The source lane now executes the real disposable first-Goal and runtime-readiness +backends rather than returning packaged-only PASS receipts. Explicitly required +backend journeys fail before PASS when their readback did not complete. Other +scenario coverage boundaries remain valid. Existing GET/HEAD socket recovery +includes EPIPE within the same three-attempt bound; HTTP writes stay single-attempt. +Research-record search checks bind help dismissal to its current keyboard trigger +and require the selected query to survive dismissal. + +On immutable source `eedfc5e24`, local macOS/Node26 qualification passes all +86 scenarios across three non-overlapping 29/29/28 partitions with a common +catalog and source tree, verified backend-readback markers and nonempty merged +coverage. The passing partitions take 1,943.74 seconds; earlier failed runs, +setup and repair remain separate records. This is validation cost, with no +complete model/service/human cost comparison. + +The matched 200-item M2 pair retains all 15 samples and long-task windows. +Ordinary medians are 225.77/172.99/310.77/128.45/146.52 ms, maximum 366.08 ms; +coverage medians are 328.21/371.20/332.49/174.73/173.02 ms, maximum 421.22 ms. +Both have zero samples above the 1-second target. Full-suite coverage M2 also +passes: medians 325.35/270.28/326.59/164.22/166.47 ms, maximum 463.88 ms. +The real 1,200 ms repeated Board delay is correctly rejected by ordinary +qualification (slow median 1491.77 ms, maximum 1519.98 ms; all 15 +samples/windows retained). Existing strict-boundary and missing-sample tests +pass without a budget change. + +The failed source partitions and historical frozen failures retain their original +status. This is named local source qualification, not Linux CI, an installed-host +upgrade, model task completion, kernel attribution or full-cost acceptance. V10 +thresholds and the remaining real-team/product milestones retain their owners. + **Checkpoint 2026-10-11 (M1 proposed at `6c3c0c104` in [#6191](https://github.com/loopx-project/loopx/pull/6191), subsequently merged).** Verified subset: V1 reducer fixtures written from Section 5.3, mutation checks and the packaged page on synthetic status; V2 strip and Board sharing the work diff --git a/docs/architecture/rfcs/goal-workspace-surface-v0.zh-CN.md b/docs/architecture/rfcs/goal-workspace-surface-v0.zh-CN.md index b621e4f368..bb885e90c5 100644 --- a/docs/architecture/rfcs/goal-workspace-surface-v0.zh-CN.md +++ b/docs/architecture/rfcs/goal-workspace-surface-v0.zh-CN.md @@ -513,6 +513,30 @@ M3、W2/W3、安装态以及 L1/G1 的真实交付和原请求路线返回仍由 全局 Enter 到达前被重渲染替换。验证仍须在既有展开预算内展示全部 200 项。这项证据 验证浏览器驱动的目标选择;产品焦点恢复及早期部分清单的成因仍需各自的证据。 +**检查点 2026-11-12(源码 coverage 执行)。** +源码分线现在执行真实的一次性首次 Goal 与运行就绪后端,不再提前返回仅适用于打包 +模式的 PASS 回执。明确需要后端的旅程未完成读回时,在 PASS 前失败;其他场景的 +合理 coverage 边界仍有效。既有 GET/HEAD socket 恢复纳入 EPIPE,仍至多三次; +HTTP 写请求只尝试一次。 +研究记录搜索检查将提示关闭绑定当前键盘按钮,并要求关闭后保留原查询。 + +固定源码 `eedfc5e24` 在本地 macOS/Node26 下完成全部 86 场景资格:三个互不重叠的 +29/29/28 分片共享同一完整场景目录和源码 tree,后端读回标记及非空合并 coverage 均已核对。 +本次通过的分片用时 1,943.74 秒;此前失败运行、准备和修复仍分开记录。这是验证成本, +不构成完整模型、服务和人工成本比较。 + +同版本 200 项 M2 对照保留全部 15 个样本和长任务窗口。普通模式五组中位数为 +225.77/172.99/310.77/128.45/146.52 ms,最大 366.08 ms;coverage 模式为 +328.21/371.20/332.49/174.73/173.02 ms,最大 421.22 ms,两者超过 1 秒目标的 +样本数均为 0。完整分片中的 coverage M2 也通过:中位数 +325.35/270.28/326.59/164.22/166.47 ms,最大 463.88 ms。真实注入 1,200 ms 的重复看板 +停顿被普通资格正确拒绝:慢组中位数 1491.77 ms、最大 1519.98 ms,全部 15 个 +样本和窗口仍保留。既有严格边界及缺失样本负例通过,预算未变。 + +源码分片失败及历史冻结失败保留原判定。这是指定本地源码的资格,不证明 Linux CI、 +安装宿主升级、模型任务完成、内核归因或完整成本验收。V10 阈值及剩余真实团队和产品 +里程碑仍由各自既有 owner 验收。 + **检查点 2026-10-11(M1 在 [#6191](https://github.com/loopx-project/loopx/pull/6191) 的 `6c3c0c104` 提出,后续已合并)。** 已验证的子集:V1 的按第 5.3 节编写的 reducer fixture、变异检查及合成状态打包页面; V2 的指标条与看板共用工作判定;V4 的真实隔离 Chat 动作服务与注册表 authority 上的停止、 diff --git a/docs/architecture/rfcs/monorepo-distribution-split-v0.md b/docs/architecture/rfcs/monorepo-distribution-split-v0.md index 2021df3c6c..d6976e2071 100644 --- a/docs/architecture/rfcs/monorepo-distribution-split-v0.md +++ b/docs/architecture/rfcs/monorepo-distribution-split-v0.md @@ -3468,3 +3468,44 @@ qualifies the consumed replan read cost; full CLI process, durable writeback, model and end-to-end outcome costs are not inferred from these timings. The complete minimal load/context closure, R1/M3, independent Standard packages, workspace/meta publication and installed-host adoption remain open. + + +### 2026-11-12 — Dashboard source acceptance executes its backend journeys + +Source coverage now executes the existing first-Goal and runtime-readiness +journeys through the real disposable Chat service. Both source and packaged +lanes already prepare the verified Chat bundle. Declared backend journeys +require successful backend assertions before a PASS receipt; an early return +cannot qualify them. Scenarios with other legitimate coverage boundaries keep +their existing behavior. The shared browser proxy adds EPIPE to its existing +three-attempt GET/HEAD socket recovery, recognizes typed errno or the first +Playwright socket-error line, and never retries an HTTP write or arbitrary prose. +Research-record search checks bind help dismissal to its current keyboard trigger +and require the selected query to survive dismissal. + +On immutable source `eedfc5e24`, local macOS/Node26 qualification passes all +86 scenarios across three non-overlapping 29/29/28 partitions with a common +catalog and source tree, verified backend-readback markers and nonempty merged +coverage. The passing partitions take 1,943.74 seconds; earlier failed runs, +setup and repair remain separate records. This is validation cost, with no +complete model/service/human cost comparison. + +The matched 200-item M2 pair retains all 15 samples and long-task windows. +Ordinary medians are 225.77/172.99/310.77/128.45/146.52 ms, maximum 366.08 ms; +coverage medians are 328.21/371.20/332.49/174.73/173.02 ms, maximum 421.22 ms. +Both have zero samples above the 1-second target. Full-suite coverage M2 also +passes: medians 325.35/270.28/326.59/164.22/166.47 ms, maximum 463.88 ms. +The real 1,200 ms repeated Board delay is correctly rejected by ordinary +qualification (slow median 1491.77 ms, maximum 1519.98 ms; all 15 +samples/windows retained). Existing strict-boundary and missing-sample tests +pass without a budget change. + +Earlier failed source partitions (EPIPE, query loss and temporary-directory +failure) remain recorded separately, as do historical frozen 1.162-second +failures and cancelled runs. This repairs +source-acceptance execution and its bounded transport recovery, with no render +threshold, kernel runtime or authority change. The first-Goal fixture uses an +exit-only Agent: registration and honest failed-Turn recovery do not prove +natural-language task completion or model benefit. Complete minimal closure, +R1/M3, workspace/meta publication, installed-host adoption and full-cost +acceptance remain open. diff --git a/docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md b/docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md index b38cf52a99..0159df1b5a 100644 --- a/docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md +++ b/docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md @@ -2706,3 +2706,33 @@ dry-run 拒绝路径的中位数为 142.4→135.1 ms、934.4→911.8 ms。零条 这些计时推断完整 CLI 进程、持久写回、模型及端到端成果成本。完整最小加载/ 上下文闭包、R1/M3、独立 Standard 包、workspace/meta 发布及安装宿主采用仍未 完成。 + + +### 2026-11-12 — Dashboard 源码验收执行真实后端旅程 + +源码 coverage 现在通过真实的一次性 Chat 服务执行既有首次 Goal 和运行就绪旅程。 +源码与打包分线均已准备经校验的 Chat bundle。声明需要后端的旅程必须完成真实后端 +断言后才能记录 PASS,提前返回不能满足该条件;其他有合理 coverage 边界的场景 +保留原有行为。共用浏览器代理在既有至多三次 GET/HEAD socket 恢复中纳入 EPIPE, +仅识别类型化 errno 或 Playwright 首行 socket 错误,不重试 HTTP 写请求或任意诊断文字。 +研究记录搜索检查将提示关闭绑定当前键盘按钮,并要求关闭后保留原查询。 + +固定源码 `eedfc5e24` 在本地 macOS/Node26 下完成全部 86 场景资格:三个互不重叠的 +29/29/28 分片共享同一完整场景目录和源码 tree,后端读回标记及非空合并 coverage 均已核对。 +本次通过的分片用时 1,943.74 秒;此前失败运行、准备和修复仍分开记录。这是验证成本, +不构成完整模型、服务和人工成本比较。 + +同版本 200 项 M2 对照保留全部 15 个样本和长任务窗口。普通模式五组中位数为 +225.77/172.99/310.77/128.45/146.52 ms,最大 366.08 ms;coverage 模式为 +328.21/371.20/332.49/174.73/173.02 ms,最大 421.22 ms,两者超过 1 秒目标的 +样本数均为 0。完整分片中的 coverage M2 也通过:中位数 +325.35/270.28/326.59/164.22/166.47 ms,最大 463.88 ms。真实注入 1,200 ms 的重复看板 +停顿被普通资格正确拒绝:慢组中位数 1491.77 ms、最大 1519.98 ms,全部 15 个 +样本和窗口仍保留。既有严格边界及缺失样本负例通过,预算未变。 + +此前源码分片的 EPIPE、查询丢失和临时目录失败分别保留记录;历史冻结的 1.162 秒失败 +及已取消运行也保持原判定。 +本项修复源码验收执行与有界传输恢复,不改变渲染阈值、内核运行时或 authority。 +首次 Goal 夹具使用退出式 Agent:登记及如实显示失败 Turn 的恢复不证明自然语言任务 +完成或模型收益。完整最小闭包、R1/M3、workspace/meta 发布、安装宿主采用及完整成本 +验收仍保持开放。