Skip to content

main 上 CI 不稳:computer-agent「stdin 关闭后代理自己停机」在 Linux 上间歇超时 #79

Open

Description

@joesmart

现象

main 最近 4 次 push 的 CI 有 3 次红,PR 上的同一提交却是绿的。

  • run 39904552735(025039e)、run 39703361940(cc57b80):packages/computer-agent/test/process.test.ts >「电脑代理进程 > stdin 关闭后代理自己停机」,等了 5000ms 进程还没退出(第 275 行)。
  • run 39767590315(0fb90f8):packages/bot-runner/test/runner.test.ts >「助理浏览器(真 Chrome)> 截图进模型是图片,截的时候密码框是空的,截完填回去」。

pnpm -r test 第一个包红了就停,后面的包(含 e2e)在这几次里没跑到。

影响

每个 PR 合进 main 之后都可能把 main 染红,CI 绿不再能当合并依据。

🤖 Generated with Claude Code

Activity

  1. joesmart commented on Oct 23, 2026

    @joesmart
    ContributorAuthor

    补一条相关但根因不同的不稳用例,记在这里,#88 不修它。

    现象:PR #80 的 CI(run 39954203740 第一次)红在 packages/control-plane/test/team-offer.test.ts「建队卡 > 同群两张卡都确认成功,先提交的那次读群晚到,最后推的群也不少人」,报错 等A 提交之后读完群成员超时,用时 5606ms,重跑通过。

    不是「CI 慢加固定超时」:同文件其他 30 条都在 300–1900ms 之间,只差一步的「回执晚到」那条只要 524ms。屏障是直接没到,把 within 的 5 秒调大也没用。

    根因:用例用 vi.spyOn(sqlGate.db, "transaction").mockImplementationOnce(...) 两次,默认接下来两次 db.transaction 是 A 和 B 的确认。但控制面在同一个 db 上还有后台事务,比如 server.ts 每 15 秒跑一次的 settleApprovalTimeouts,它每轮都无条件开事务。如果这类事务恰好在 spy 装好之后、A 进事务之前进来:

    1. 它吃掉第一份 mock。读群屏障要等它提交才布下,committedA 也是那时才调。
    2. A 拿到第二份 mock,跑完整个请求,publishGroup 的读群也做完了。
    3. 后台事务这时才提交、布下屏障,可之后再没有人读群,于是等满 5 秒超时。

    复现:在用例副本里,spy 装好之后测试自己先在 sqlGate.db 上开一个事务,扣到 A 回 200 再提交。稳定得到同样的 等A 提交之后读完群成员超时(5334ms)。时间上也吻合:失败那次文件约 12:36:46.7 开始,失败用例约 12:37:03.4 开始,正好是服务器起来后第 15 秒那一轮扫描附近。

    修法方向(另开线做):mock 只认 A、B 自己的事务,比如在包装里按事务内跑到的 SQL 或参数(卡 id)认人,别人的事务直接透传。或者让这个文件的测试服务器把审批到期扫描的周期调到用不着(approvalTimeoutIntervalMs)。同文件「回执晚到」那条也是一样的 mockImplementationOnce 写法,有同样的风险。

  2. joesmart commented on Oct 23, 2026

    @joesmart
    ContributorAuthor

    再补一条:control-plane team-offer.test.ts 屏障等待超时

    • 用例:packages/control-plane/test/team-offer.test.ts >「建队卡 > 同群两张卡都确认成功,先提交的那次读群晚到,最后推的群也不少人」
    • 报错:Error: 等A 提交之后读完群成员超时(test/helpers/fixtures.ts:34 的 waitFor,调用点 team-offer.test.ts:1144 的 within,用例第 1122 行等 gate.reached)。
    • 出现在 PR 控制面定期清理过期会话 (#27) #80 的 run 39954203740 第一次跑。这个 PR 只改了会话清理(repo/sessions.ts、server.ts),碰不到建队卡的路径。本机 macOS 上整包是绿的,同一个 run rerun --failed 后就过了。
    • 现象:A 的确认已经提交,但读群的屏障 5 秒内没到。看起来是 CI 慢机上一个固定 5 秒的等待窗口不够,或者交错没按预期走,还没细查。

    🤖 Generated with Claude Code

  3. joesmart commented on Oct 23, 2026

    @joesmart
    ContributorAuthor

    再记一条,根因也和本 issue 不同,#88 不修。

    现象:PR #84 只改了 web,CI run 39960076349 红在 packages/control-plane/test/run-end.test.ts:1021「同 Bot 同群的收尾、建 run、删 Bot 排队 > 删 Bot 已锁住它的 run 之后才建的群里派给它:建 run 等删除提交,Bot 已不在群里就不再给它建,删除回 204 不死锁」。DELETE /api/bots/:id 回了 500 {"error":"服务端出错"},期望是 204。

    不是超时:这条用例总共 440ms,没有等满任何墙钟。同一时刻 stderr 里打出:

    PostgresError: insert or update on table "runs" violates foreign key constraint "runs_trigger_message_id_messages_id_fk"
    code: '23503'
    detail: 'Key (trigger_message_id)=(3485510d-…) is not present in table "messages".'
    

    初步判断(未复现,未核实):删 Bot 的路由在 deleteBot 提交之后会调 groupTurns.botDeleted,把这个 Bot 的回合重新派给群里还在的 Bot,也就是在 DELETE 请求里插 runs。用例里同时有一条发往新群的消息(posting)正在走派活,它被删除的锁挡着。重新派活拿到的触发消息 id,在插 run 的时候库里已经没有了:要么那条消息的事务回滚了,要么还没提交就被引用了。这是删 Bot 和派活之间真实的并发窗口,产品代码有可能回 500,不只是测试写法的问题。需要另开一条线,从 agent/group-turn.ts 的 botDeleted 和 dispatch.ts 的建 run 往下查。

  4. joesmart commented on Oct 23, 2026

    @joesmart
    ContributorAuthor

    补一条不稳用例(PR #88 复核时发现,main 上本来就有):

    packages/computer-agent/test/chrome.test.ts「代理进程被 SIGKILL 后它的 Chrome 跟着退……」。本机 36 个 busy loop 下跑整包,merge-base 025039e 5 轮红 4 轮,PR #88 head 5 轮红 2 轮。报错是 等了 10000ms 还没等到管道断开后旧 Chrome 自己退出。负载高时 10 秒等不到 Chrome 退出,根因还没查。

    另外两条测试缺口,都不是 #88 引进的:

    • main.ts 里「launch 解析前 stdin 已经结束」那条分支(if (stdinEnded) stop();),现在没有用例覆盖,删掉它测试照样是绿的。
    • process.test.ts 第 282 行用例超时和 waitForExit() 默认值都是 60_000。真挂死时 vitest 先超时,finally 里的 SIGKILL 就不执行了。

    🤖 Generated with Claude Code

    https://claude.ai/code/session_01Be3SZps2JjqvyrJ1vbLmni

  5. joesmart commented on Oct 24, 2026

    @joesmart
    ContributorAuthor

    删 Bot 偶发 500(23503,runs_trigger_message_id_messages_id_fk)已由 #99 修掉,提交 469924c、730ba30。

    根因:一条 DELETE FROM bots 经几条级联先后 UPDATE 同一行 run(bot_id、单聊群删除带出的 group_id、单聊消息删除带出的 trigger_message_id)。PostgreSQL 对本事务改过的行再改时重查全部外键,触发消息已删、它的置空还没轮到就报 23503。先后由触发器名里的 OID 决定,CI 每个测试库新建,所以偶发。原先猜的「重派引用未提交消息」不成立。

    修法:deleteBot 在删 Bot 行之前、同一事务里显式置空这三列。复核时把相关外键的创建顺序全排列(120 种)跑过,原删法 60/120 炸,修后 0/120。新用例 test/bot-delete-cascade.test.ts 重建约束固定触发顺序,还原修复前两条都红。

    这个 issue 里其余的不稳用例不在 #99 的范围内,issue 保持开。

    🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions