Tuning tman for go test

Go

Silent for minutes while compiling; keep Go's own -timeout below tman's.

capstart atwhat it answers
stall15mhang backstop — no output and no CPU/IO activity for this long
max-time20mwall-clock budget — the only cap that bounds a run that is working but will never finish
max-memunset — opt in only if it has hurt youRSS ceiling across the process tree
max-parallel2slots per name-and-directory bucket

These are starting points derived from how go test 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 "test" {
    command "go"
    args "test" "-timeout" "10m" "./..."
    max-time "20m"
}

go test ./... compiles every package before it runs anything, and a cold build cache means minutes of complete silence. This is the case that motivated tman's activity-aware stall detection: across 959 supervised runs, a 60-second stall backstop fired 32 times and caught no actual hang — it killed go build ./... at 60s on work that succeeded 14 other times, once taking 75 seconds.

That is why the built-in stall is 30m and why the guidance is to size it well above the longest quiet stretch you ever expect. On Linux the compile is protected regardless — CPU jiffies advance, so tman sees a busy tree even with no output. On macOS and Windows only the root process is sampled, so the wide default is doing the work.

Let Go's timeout fire first

go test -timeout and tman's max-time answer the same question, but they answer it very differently. Go's panics the test binary and prints a full goroutine dump naming exactly what was blocked. tman's kills the tree and returns 124. The dump is worth far more, so set Go's below tman's and let it win:

layervaluewhat you get
go test -timeout10mgoroutine dump naming the blocked test — diagnose from this
max-time20mbackstop for a binary too wedged to panic, or a hang before the tests start
stall15mcatches an idle tree; the compile is covered by activity detection on Linux

The same layering applies to gotestsum and to -race, which roughly doubles runtime and memory — give a race build its own alias with its own max-time rather than widening the ordinary one to fit it.

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 did tman kill my go build with exit 125?

It did not, at the current defaults. The stall backstop is 30m and on Linux a compile counts as activity because CPU jiffies advance even with no output. If you see 125 on a Go build, you are on a config carrying the old 60s stall, or on macOS or Windows where only the root process is sampled — widen `stall`.

Should I set go test -timeout if tman already has max-time?

Yes, and lower. Go's timeout panics the test binary and prints a goroutine dump naming what was blocked; tman's kills the tree and reports 124. Setting Go's below tman's means you get the diagnosis in the normal case and keep the backstop for a binary too wedged to panic.

Sources