Skip to content

main push CI 两处时间型 flake:follow-through 群回合恢复 settle / social hide-remote 等 room leave #2102

Closed

Description

@Birfy

现象

main 上的 push 版 Test 连续两条提交变红,但同一批改动进 merge queue 的 merge_group Test 是绿的,之后再一条 push(1778add0)又全绿——即两个时间型 flake,不是回归:

1) 1840b05e22535e143c013e9b54695869fc08ccfc(02:02:24Z push)

  • run 41375553471,失败 job:ubuntu-latest · Node 24 · DSH 0.2.0-rc.2(125054885184)、tests-ok(125055499597)
  • 失败用例:core/test/follow-through.test.js:164 — "a group turn cut off by a restart is picked up once, and the group is told"
  • 报错:AssertionError [ERR_ASSERTION]: condition not reached,栈在 settle (test/follow-through.test.js:137:42)

2) c4e425f9e8d7ab63addc5ad8a397f1363cd8b7ea(02:07:49Z push)

  • run 41376032435,失败 job:plugins (social)(125056353099)、tests-ok(125056933492)
  • 失败用例:plugins/social/test/social.test.js:5391 — "[Team] Remove a friend's Bot, and approve held Bot replies inside the chat #90: hide-remote leaves independent rooms in parallel and does not wait for them"
  • 报错:Error: hide-remote waited for room leave(social.test.js:5424:56,Timeout._onTimeout),541 tests / 540 pass / 1 fail

为什么要开单

  • 1778add0ac(当前 main)同一条链全绿,说明两处都是等待条件写太紧(固定 timeout / 轮询 settle),只在 CI 慢机偶发。
  • 但 main 红会直接影响两件事:每日发布前的「最新 Test 绿」判定,以及外人看主分支时的可信度——每红一次都得人工复跑确认。

可选修法(待评估)

  1. 把两处固定等待改成有上限的轮询(与仓库里已有 eventually 类 helper 对齐),而不是一次性 condition not reached;
  2. 或者 CI 层对 push 的 Test 加一次自动重跑(只在 push 事件、且失败用例属于已知 flake 名单时),避免 main 被噪声染红;
  3. 两者都要有「把实现改坏仍然会红」的证据,不能只把等待放宽。

证据

Activity

  1. Birfy commented on Nov 2, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Triage

    Triage — agent-ready,只做选项 1,选项 2 排除在本单之外

    两处都是测试侧等待写死(不是产品回归):main 1778add0 同一条链全绿,且两处失败的等待都自带可疑模式。最小修法 = 把「等多久」换成「等什么事件」,不碰任何产品代码。

    范围(test-only,两个文件,别扩)

    1. core/test/follow-through.test.js
      waitFor()(第 112–136 行)在 mocked 分支里是固有 5 秒自旋:for (let i = 0; !check() && (i < rounds || Date.now() < deadline); i++) { mockedTimers.tick(1); await setImmediate }。条件没满足时它并不"等",而是把 20000 轮 setImmediate + tick 烧满 REAL_WAIT_MS=5000——在 2 核 CI 上这 5 秒 CPU 正好跟它要等的真实工作(inflight.json/state.json 写盘、Session flush、resolve Agent)抢核,于是越等越慢,settle() 抛 condition not reached(栈 test/follow-through.test.js:137:42)。
      改法:把自旋换成真让出 CPU 的步进——文件里已有 const realSetTimeout = setTimeout(第 98 行,mock.timers 不会冻结它,第 528 行已在用):每步 await new Promise(r => realSetTimeout(r, REAL_WAIT_STEP_MS)) 并 mockedTimers.tick(1),删掉轮数下限、只保留真实时钟 deadline,budget 提到 15000ms。语义不变(真丢的调用照样超时失败),只是不再抢 CPU。

    2. plugins/social/test/social.test.js — #90: hide-remote leaves independent rooms in parallel…(第 5391 行)
      用 Promise.race([..., setTimeout(reject, 60)])(5424) + assert.ok(Date.now() - started < 60)(5427) 判"没有等 room leave"。60ms 墙钟本身就在测调度延迟:慢机上即使实现完全正确也会红(报 Error: hide-remote waited for room leave)。
      改法:跟同文件第 5353 行的兄弟用例一样用门闩证明非阻塞——把 room leave 挡住(hide-remote 走的 leave 路径上挂一个 held promise,参照 5357/5382 的写法),断言 hide-remote 在门闩未放开时就返回 leftRooms 含两个 room,再 release() 让 waitUntil()(第 188 行已有 helper)等到 leftSeq 落定。删掉 60ms 的 race 与墙钟断言。
      顺带:同一文件 5382 行那个 200ms race 属同类,若一行能顺手换掉就换,改不动不必强求(不算本单失败)。

    不做:选项 2(push 的 Test 自动重跑 + 已知 flake 名单)是 CI 策略变更,需要 owner 拍板,另开单;本单不合入任何 workflow 改动。

    验收(可执行,红先)

    • node --test core/test/follow-through.test.js 与 cd plugins/social && node --test test/social.test.js 各连跑 30 遍全绿;再 npm run verify 全绿。
    • 反证(必须贴日志):
      • follow-through:把重启后的 pickup 打断(例如 src/host/turns.js 里 resume 那段短路 / 注释掉 openRoomTurn 的重放),用例仍然要在 deadline 内红——证明放宽的是等待方式不是判定条件;
      • social:让 hide-remote 先 await room leave 再返回,5386/5429 这两条断言必须红。
    • 贴 CI 复跑:改完后对同一分支连推两次让 push Test 跑两遍,两次绿。

    风险档

    diff 只在 core/test/follow-through.test.js + plugins/social/test/social.test.js,无产品代码、无 workflow,预期 T1(tests-ok 绿 + 可合并即可,无 PASS);tier 以 risk-gate.mjs 实算为准。

  2. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Nov 2, 2026
  3. self-assigned this
    on Nov 2, 2026
  4. added
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Nov 2, 2026
  5. Birfy commented on Nov 2, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Dev

    Dev — 认领,分支 dev/2102-time-flake-tests

    按 Triage 的范围只改两个测试文件,不碰产品代码、不碰 workflow:

    1. core/test/follow-through.test.js — waitFor() mocked 分支去掉 20000 轮 setImmediate 自旋,改为 realSetTimeout 步进 + 每步 tick(1),只留真实时钟 deadline(15s)。
    2. plugins/social/test/social.test.js — hide-remote 用例改门闩(held/release + waitUntil),删掉 60ms race 与墙钟断言。

    验收按单上执行:两文件各 30 遍 + npm run verify + 两份反证日志(打断 resume / 让 hide-remote await leave 必须红)+ 分支连推两次 push Test 绿。选项 2 不做。

    Base: main 1778add0ac

  6. removed
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Nov 2, 2026
  7. removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions