@@ -235,6 +235,11 @@ jobs:
235235 needs : prepare
236236 if : needs.prepare.outputs.needs_deployment == 'true'
237237 runs-on : ubuntu-latest
238+ # Backstop only: the real hangs are bounded per-step (wait_for caps at ~10m,
239+ # the browser install retries with a 180s/attempt cap). This guards against
240+ # an unexpected hang without cutting off a legitimately slow run (deploy
241+ # readiness wait + cold browser install can take ~20m).
242+ timeout-minutes : 30
238243 steps :
239244 - name : Wait for deployed web and API
240245 env :
@@ -281,7 +286,7 @@ jobs:
281286 if : steps.auth_bootstrap.outputs.enabled == 'true'
282287 uses : actions/setup-node@v4
283288 with :
284- node-version : " 24 "
289+ node-version : " 22 "
285290
286291 - name : Install pnpm
287292 if : steps.auth_bootstrap.outputs.enabled == 'true'
@@ -294,10 +299,50 @@ jobs:
294299 working-directory : web
295300 run : pnpm install --no-frozen-lockfile --filter agenta-web-tests...
296301
302+ # Cache the downloaded browsers. On a cache hit `playwright install` is a
303+ # no-op, which avoids the chromium download entirely — and that download
304+ # is what stalls: the debug trace showed the 170 MiB transfer hitting 100%
305+ # in ~2s, then the install hanging (no progress) until killed. apt deps
306+ # were never the problem (they finished in ~10s).
307+ - name : Cache Playwright browsers
308+ id : pw-cache
309+ if : steps.auth_bootstrap.outputs.enabled == 'true'
310+ uses : actions/cache@v4
311+ with :
312+ path : ~/.cache/ms-playwright
313+ key : playwright-${{ runner.os }}-${{ hashFiles('web/pnpm-lock.yaml') }}
314+ restore-keys : |
315+ playwright-${{ runner.os }}-
316+
317+ # OS libraries for chromium. This is the fast, reliable part (~10s); kept
318+ # as its own step so a browser-download stall can't be confused with it.
319+ - name : Install Playwright system dependencies
320+ if : steps.auth_bootstrap.outputs.enabled == 'true'
321+ working-directory : web/tests
322+ run : pnpm exec playwright install-deps chromium
323+
324+ # Browser binaries. Root cause of the original ~6h hang: Playwright 1.59's
325+ # zip extraction deadlocks on Node 24 (reproduced locally: Node 22/23
326+ # extract in ~16s, Node 24 hangs indefinitely). The job is pinned to Node
327+ # 22 (LTS) above, which fixes it. The cache makes this a no-op on a hit,
328+ # and the retry + per-attempt timeout guard against any transient stall.
297329 - name : Install Playwright browser
298330 if : steps.auth_bootstrap.outputs.enabled == 'true'
299331 working-directory : web/tests
300- run : pnpm exec playwright install --with-deps chromium
332+ run : |
333+ for attempt in 1 2 3; do
334+ echo "::group::playwright install chromium (attempt ${attempt}/3)"
335+ if timeout 180 pnpm exec playwright install chromium; then
336+ echo "::endgroup::"
337+ echo "browser install succeeded on attempt ${attempt}"
338+ exit 0
339+ fi
340+ echo "::endgroup::"
341+ echo "attempt ${attempt} stalled or failed; retrying after 5s..."
342+ sleep 5
343+ done
344+ echo "playwright browser install failed after 3 attempts" >&2
345+ exit 1
301346
302347 - name : Bootstrap auth with global setup
303348 if : steps.auth_bootstrap.outputs.enabled == 'true'
@@ -587,7 +632,7 @@ jobs:
587632 - name : Setup Node.js
588633 uses : actions/setup-node@v4
589634 with :
590- node-version : " 24 "
635+ node-version : " 22 "
591636
592637 - name : Install pnpm
593638 uses : pnpm/action-setup@v4
0 commit comments