Tuning tman for Playwright

JavaScript / TypeScript

Browser downloads look like a hang; browsers and web servers are the orphans.

capstart atwhat it answers
stall15mhang backstop — no output and no CPU/IO activity for this long
max-time30mwall-clock budget — the only cap that bounds a run that is working but will never finish
max-mem8192RSS ceiling across the process tree
max-parallel1slots per name-and-directory bucket

These are starting points derived from how Playwright behaves, not measurements of your machine. Every one of them should be replaced by a number you took from your own runs — the last section on this page is how.

.tman.kdl
alias "e2e" {
    command "npx"
    args "playwright" "test"
    max-time "30m"
    max-mem 8192
    max-parallel 1
}

Playwright is the tool that exercises every part of tman at once: a long quiet prologue, a fleet of memory-hungry child processes, a server bound to a port, and a shutdown path that leaves things behind when it is interrupted.

The first run downloads browsers

On a cold machine or a fresh CI image, playwright test fetches browser bundles before it runs anything — hundreds of megabytes, quiet, and slow on a bad network. On Linux that traffic moves the tree's IO counters and the backstop sees a working process. On macOS and Windows it does not, so either widen stall there or, better, make the download its own step: npx playwright install --with-deps in setup, so the test alias never has to cover it.

`max-parallel 1`, always

Playwright's webServer binds a port. Two runs of the same suite in the same directory means two servers wanting the same port, and the failure is not clean — the second either bind-conflicts or quietly talks to the first server's database and produces failures that read as flakes. One slot per bucket removes the whole category.

Browsers are where the memory is

Each worker drives a browser process with its own renderer children, and a trace or video artifact per test adds to it. max-mem 8192 is a deliberately generous ceiling: it is not there to tune throughput, it is there so a suite that leaks contexts does not take the machine down. The ceiling is summed across the tree on Linux, which is the only way it could cover a browser fleet.

Orphaned browsers are the reaper's other headline case. Interrupt a Playwright run and you can be left with chrome processes and a webServer holding a port for the rest of the afternoon. Any tman command sweeps them: children whose runner has died are killed before anything else happens.

Turn the starting point into a real number

Everything above is derived from how this tool behaves, not from your machine. The numbers that matter come from your own runs, and getting them takes one command:

shell
tman status --json | jq '.runs[] | {name, exit, duration_ms}'

Set max-time from the slowest successful run you are willing to wait for, with real headroom — a cap that fires on healthy work teaches everyone to raise the cap, and after a few rounds of that it bounds nothing. Set stall from the longest stretch the command legitimately produces no output and no CPU, then double it.

Exit 124, 125, and 126 are findings, not flakes. 124 is a wall-clock timeout, 125 a stall, 126 a resource cull. Widening the cap that fired is the right response only after you know why it fired — the first two times a suite trips max-mem, the interesting question is what is holding the memory.

Frequently asked

Why should Playwright run with max-parallel 1?

Because its webServer binds a port. Two concurrent runs in the same directory either bind-conflict or, worse, share one server and one database and produce failures that look like flaky tests. A single slot per bucket removes that class entirely, and buckets are per directory, so other projects are unaffected.

My first Playwright run gets killed. Why?

It is probably downloading browsers — a long, quiet prologue. On Linux the IO counters cover it; on macOS and Windows tman samples only the root process, so widen stall there. The better fix is to move `npx playwright install --with-deps` into a setup step so the test alias never covers a download.

Sources