Tuning tman for Playwright
Browser downloads look like a hang; browsers and web servers are the orphans.
Recommended starting caps
| cap | start at | what it answers |
|---|---|---|
stall | 15m | hang backstop — no output and no CPU/IO activity for this long |
max-time | 30m | wall-clock budget — the only cap that bounds a run that is working but will never finish |
max-mem | 8192 | RSS ceiling across the process tree |
max-parallel | 1 | slots 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.
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
webServerholding 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:
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.