Skip to content

[Bug]: the agent-transcript case fails on the Windows runner and the failure leaves no evidence behind #32

Open

Description

@modacker

What happens

The agent-transcript integration case fails intermittently on the Windows runner: it waits for the browser panel's first frame, the frame never arrives, and the case fails after sixty seconds. It does not reproduce on demand — the same commit passed on the next attempt — and the failure currently leaves no evidence behind, which is the reason this is written down rather than fixed blind.

How it shows up

FAIL  packages/webui/test/integration/agent-transcript.test.ts > real agent rendering and recovery: transcript
Error: expect(locator).toBeVisible() failed
Locator: getByTestId('browser-panel-frame')
Expected: visible
Timeout: 60000ms
Error: element(s) not found
{"emptyFrameStillShowing":1,"capabilityNotices":[]}

The whole file takes about two minutes in that run; the vitest summary reports 1 failed | 4515 passed | 9 skipped (4525).

Evidence

  • Run 42960040230, attempt 1, Windows job: the failure above. The same commit, re-run as attempt 2, passed on all three runners — so it is a flake.
  • The failing window is exactly sixty seconds (04:38:23 → 04:39:23). Inside it the only output is a model-config network retry warning every two seconds. There is no browser output of any kind: no browser_operation_timeout, no browser_provider_unavailable, no frame, no panel notice.
  • Chrome is available on that runner: the isolated-chromium suites in the same job — isolated-chromium-two-pages (12 tests), isolated-chromium-screencast (3), isolated-chromium-profile (1) — all passed.
  • On this machine the case passed four times in a row, in 50.79 s to 57.22 s. The browser part of that is under a second (firstFrame.latencyMs = 21 ms, chrome-startup.json marks: spawned → started in 724 ms). Failing on the runner needs the browser part to take longer than the sixty seconds the case waits for.

Why the failure leaves no evidence

Two independent gaps, and the first one is why the second one matters.

The failing step's own browser diagnostics are deleted before the artifact is uploaded. The job sets WEBUI_BROWSER_OUTPUT_DIR=$RUNNER_TEMP/webui-browser-results and the Playwright configuration uses that same directory as its outputDir. Playwright's "clear output" task removes project.outputDir at the start of the browser step, which is after the units step that failed. The files that would say what the browser did — chrome-startup.json (the millisecond marks from spawned through devToolsActivePortRead to targetCreated) and service-browser.json (firstFrame.timeouts, droppedFrames, launches) — are written by the units step and are not in the artifact. The service-browser.json that is in the artifact comes from the later test:webui-package step, which writes its own.

So the four possibilities below cannot be told apart today: Chrome never started, Chrome started slowly, the frame was dropped by a generation fence, or the stream failed quickly and the panel said so.

The case's own diagnostic cannot see the states that matter. It collects two test ids — browser-panel-unknown and browser-panel-unauthorized — while the panel has six: browser-panel-absent, browser-panel-not-ready, browser-panel-unknown, browser-panel-unauthorized, browser-panel-failed (added by #28) and an error toast. browser-panel-frame-empty is the fallback branch for an empty frameSrc, and it sits beside the notices rather than instead of them. So {"emptyFrameStillShowing":1,"capabilityNotices":[]} is what "no target", "the stream failed" and "still waiting for the frame" all look like.

What would help

Inside packages/webui:

  1. Collect the panel's whole state in the case's diagnostic instead of two test ids, so one failure says which of the six states the panel was in.
  2. Let the frame and the failure notice compete. The product's first-frame deadline is sixty seconds (packages/webui/src/runtime/browser/browser-service.ts) and the case waits sixty seconds, so a browser-panel-failed notice arrives at the same moment the case gives up. Waiting for either the frame or a failure notice would turn that into an immediate, named failure.

One thing outside packages/webui, and it is the precondition for the two above: the browser diagnostics directory and Playwright's outputDir are the same directory. Giving the units step its own directory, and uploading both, would put chrome-startup.json and service-browser.json in the artifact where the next failure can be read. That is a change to .github/workflows/webui-ci.yml and playwright.config.mjs, so it is called out here rather than proposed as part of a WebUI change.

What is not the cause

  • Not a missing browser: the isolated-chromium suites passed in the same job.
  • Not the browser's own stderr being silent: the provider captures Chrome's stderr into a buffer now, so its absence from the console says nothing.
  • Not this repository's WebUI change: the case fails on a commit whose WebUI sources are the ones on main.

Note

This is a different file and a different failure from the native acceptance's teardown timeout (#30), but the same shape of problem: a Windows-only flake whose evidence the pipeline does not keep.

Activity

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