Tuning tman for ESLint
Cheap unless type-aware rules are on — then it is a typecheck wearing a linter's name.
Recommended starting caps
| cap | start at | what it answers |
|---|---|---|
stall | 5m | hang backstop — no output and no CPU/IO activity for this long |
max-time | 10m | wall-clock budget — the only cap that bounds a run that is working but will never finish |
max-mem | unset — opt in only if it has hurt you | RSS ceiling across the process tree |
max-parallel | 4 | slots per name-and-directory bucket |
These are starting points derived from how ESLint 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 "lint" {
command "npx"
args "eslint" "." "--max-warnings" "0"
max-time "10m"
max-parallel 4
}Plain ESLint is fast, bounded, and holds little memory — it barely needs supervising. Turn on type-aware rules and the picture changes completely: the linter now builds a TypeScript program, and the cost profile becomes tsc's rather than a linter's, both in time and in heap.
| configuration | what to expect | caps |
|---|---|---|
| syntactic rules only | seconds, small heap | max-time 10m is generous already |
type-aware (projectService, parserOptions.project) | a full program build; minutes on a large repo, gigabytes of heap | treat it like the typecheck guide |
Lint can take a higher `max-parallel`
The default of 2 exists because test runs contend for ports, databases, and cores. A syntactic lint contends for none of those, so a lint bucket can comfortably hold 4 slots — and because buckets are scoped per name and directory, raising it on lint does not affect the queue for test at all.
`--max-warnings 0` in the alias
Under an agent, a warning nobody reads is a warning that does not exist. Making the alias fail on any warning is the same principle as pinning --ci for Jest: a command that reports success without doing its job produces no signal to act on, so it survives indefinitely.
--cacheis worth adding for interactive use and worth omitting for the agent-facing alias. A stale cache is one more way for a lint to report success it did not earn, and the seconds it saves are not the constraint here.
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
Can lint aliases use a higher max-parallel than test aliases?
Yes. The default of 2 is sized for runs that contend for ports, databases, and cores; a syntactic lint contends for none of them, so 4 is comfortable. Buckets are scoped per name and directory, so raising it on the lint alias leaves the test queue untouched.
Why is my ESLint run suddenly taking minutes and gigabytes?
Almost certainly type-aware rules. With `projectService` or `parserOptions.project` set, ESLint builds a full TypeScript program, and the cost profile becomes tsc's rather than a linter's. Size the caps from the typecheck guide instead.