Test the first progress line against the held stream, not a wall clock (BUG-1468)
Independent Staging Quality Gate / validate (push) Successful in 16m12s
Independent Staging Quality Gate / publish (push) Successful in 3m57s

The 300 ms budget failed twice on the gate runner while the test takes about
74 ms locally. What it proves is that the line shows before the server
answers, so assert that the controllable stream has not answered yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Ad9rqAB8pXXvyzh7f4X8v
This commit is contained in:
Jesse_Chen
2026-10-28 05:41:04 +08:00
co-authored by Claude Opus 5.5
parent 0f0fea325e
commit 7444964859
2 changed files with 25 additions and 2 deletions
+16
View File
@@ -20017,3 +20017,19 @@
- 相关记录:BUG-1466、BUG-1433、BUG-989(新对话首问才落库)、BUG-725 / BUG-1329(外壳拿稳定包装、调用最新回调);TASK-sidebar-unify-20260916 D1(只读侧栏的约定,本条推翻其中「别的页面不写」一项,产品 2026-10-27 选 A 授权)。
- 复发自:无
- 修复版本:分支 `codex/session-shell-116-20261027`。
## BUG-1468 | staging 门禁两次挂在「发送后 300 毫秒内出第一句」墙钟断言
- 状态:resolved(自动化)
- 首次发现:2026-10-28(`0f0fea325` 的 backend-quality-gate run id 5730,首跑与重跑都失败)
- 最近更新:2026-10-28
- 影响面:只影响门禁;产品行为无变化。
- 用户现象:无。门禁 5274 条只失败 1 条:`rectification-latency-20260926.test.tsx` 的 D3,报「first progress line within 300 ms of send」。部署因此没有发生,staging 停在 `36fd4b2c8`。
- 触发条件:CI 机器较慢时。
- 根因:断言用墙钟量「发送到第一句出现」不超过 300 ms。它要证的是第一句不等服务端,而墙钟在共享 CI 机上不可靠。本机基线和新版都约 74 ms;`CI=1` 本机全量通过;该测试只渲染校正对话组件,不渲染 `0f0fea325` 改过的任何文件。
- 修复:去掉墙钟,改为断言此刻服务端回复还没交给页面(`controllableStream().answered === false`)。上一行「第一句已在屏上」的断言保留。三栏说明在测试里。
- 验证:该文件 2 条通过;若页面改成等服务端才出第一句,上一行 `deepEqual` 即失败。
- 防复发:组件测试不用墙钟预算证明「先于服务端」,用可控流的状态证明。
- 相关记录:BUG-1042~1047(该测试来源)、BUG-1467。
- 复发自:无
- 修复版本:分支 `codex/session-shell-116-20261027`。
@@ -229,10 +229,14 @@ function controllableStream() {
let controller!: ReadableStreamDefaultController<Uint8Array>;
const body = new ReadableStream<Uint8Array>({ start(c) { controller = c; } });
let release!: () => void;
let answered = false;
const gate = new Promise<void>((resolve) => { release = resolve; });
return {
/** True once the server reply has been handed to the page. */
get answered() { return answered; },
respond: async () => {
await gate;
answered = true;
return new Response(body, { status: 200, headers: { "content-type": "application/x-ndjson; charset=utf-8" } });
},
release: () => release(),
@@ -272,11 +276,14 @@ test("D3 · the first stage line shows on send (before any server byte), then fo
},
});
try {
const sentAt = Date.now();
await chat.type("2023 年 3 月换了工作");
// The fetch has not resolved yet: this line is on screen with no server byte.
assert.deepEqual(shown(chat), [LABELS.received], chat.text());
assert.ok(Date.now() - sentAt <= 300, "first progress line within 300 ms of send");
// 原值:assert.ok(Date.now() - sentAt <= 300, "first progress line within 300 ms of send")
// 新值:断言此刻服务端回复还没交给页面(stream.answered === false)
// 原因:要证的是「第一句不等服务端」,墙钟在共享 CI 机上不可靠:本机 74 ms,
// 0f0fea325 的门禁两次在这里超 300 ms,而该测试不渲染那次改动的任何代码(BUG-1468)。
assert.equal(stream.answered, false, "first progress line shows before the server answers");
assert.equal(chat.text().includes("正在处理…"), false);
stream.release();