Repository navigation
Releases: SpoBo/taskguard
Release list
taskguard v0.11.0-rc.2
Added
taskguard topshows its version at the right of the key bar, in the
room the keys leave:taskguard VERSION, orvVERSIONwhen narrow.
Prebuilt binaries for macOS (Apple silicon, Intel) and Linux (x86_64, ARM, static musl). Each archive holds the taskguard binary; a .sha256 file sits next to it.
taskguard v0.11.0-rc.1
Added
taskguard dump PATTERN | --cmd "COMMAND" | --run IDprints everything
taskguard knows about a job as one JSON document: how the command matches,
the rules in force and which file set them, what the next run reserves and
why, step by step (history, boost, first guess, caps, minimums), each recent
run with what it waited for, what it changed, and its role in the need
(outlier, failed and ignored, boost), pruned runs, and the live queue entry.
Use it to find out why a job reserved what it did.- Each run now stores how its need was made (
runs.need_why), sodump
can explain an old run too, not only the next one. - A long-lived job can declare the work that will run on it:
--expect WORK(repeatable) orTASKGUARD_EXPECT=WORK,WORK, where WORK is
a pool name, a job key or a command pattern. The stack books room for that
work (learned as the work learns its own need; for a pool, the need its
jobs typically started with) and starts only with room for both. A job in the same checkout that matches is its work: it goes one
priority above the stack, takes over the booked room when it starts, and
waits while its stack still waits.taskguard statusshows(for stack KEY)on the work and(expects WORK, books SIZE)on the stack. Without a
declaration, only what the history infers applies (infer_work, below). - A long-lived job with no declared work books the work its history says
follows it: work of one kind (a pool, or a key outside any pool) that
queued in its checkout withininfer_work_within(900) seconds of its
start on at leastinfer_work_min(3) of its lastinfer_work_of(5)
runs there. It books that work's usual need, shown as(expects WORK from history, ...)until work queues for it, for twice its usual delay and ten
minutes at most, and drops it once work for the stack starts. While the
guess is open the stack waitsexpect_waitfor the work, and matching work
links to it and goes first; an expired or started guess links nothing, and
work linked to it drops back to its own priority. A guess whose work has queued and
linked to it came true: it is declared work from then on, also for the
rule for nothing running, and does not expire while that work waits. The
guess never stops the rule for nothing running. Another stack respects it
while it is open, also one started with no declared work, so such a stack
waits at most until the guess expires or its work starts; a stack that
still fits next to it starts at once. A declaration wins;--expect none
expects nothing;infer_work = falseturns it off. expect_wait(5 seconds by default, 0 turns it off): a long-lived job that
expects work, declared or from its history, waits that long before it
starts for the work to queue, so a stack and a run started together start
with room for both. A stack that expects nothing starts at once.
Changed
- With nothing running, or nothing but long-lived jobs past their start-up,
the first job in line that fits starts, up to all of RAM, rather than the
first in line whatever the memory reading says. Next to long-lived jobs, a
long-lived job, such as a second dev stack, starts that way only under
mem_max, or, when only programs outside taskguard keep it out, up to the
ceilingoutside_admitallows. With nothing of taskguard's running, it
starts up to all of RAM, as before. A job that does not fit holds nobody
back, so the jobs behind it that fit start. At macOS warning this rule
still decides when only long-lived jobs run. - A waiting job that would not fit even once every job that ends by itself
has ended holds nobody back, whatever its priority or reservation: it can
only start once a long-lived job is stopped or other programs free memory,
and the small jobs behind it start meanwhile. - A stack with work queued for it, and work whose stack runs, hold their turn
as a strict reservation, also while they do not fit yet, rather than letting
jobs that fit start meanwhile (backfill).
Fixed
- A second dev stack is no longer started over memory while the first one's
run waits: the rule for "nothing runs but long-lived jobs" started the head
of the line whatever the readings said, and skipped itself at macOS
warning, so the run waited until its caller gave up. - The NEXT line names a long-lived job that holds the room as one that must
be stopped, not as one that will finish. outside_admitworks again whenmem_maxis at or abovepause_at
(92% by default), as withmem_max = 95. It capped memory atpause_at,
under the limit, so it could never let a job start; the cap is now
halfway frommem_maxto all of RAM in that case.
Prebuilt binaries for macOS (Apple silicon, Intel) and Linux (x86_64, ARM, static musl). Each archive holds the taskguard binary; a .sha256 file sits next to it.
taskguard v0.10.0
Fixed
- A run that failed no longer raises a job's memory need above what its
passing runs took, also with fewer than three runs and when it is no
outlier. Onetscrun that failed at 14.5 GB made every later run reserve
18 GB. When every recent run failed, one high peak alone does not set the
need: the job reservesnew_job_memuntil a peak comes back. - A failed run starved of memory adds the 25% boost only when its peak stayed
within that 25% of its need. taskguard outliers,doctor --explainand the job's end line show failed
runs whose peak is ignored, and why.
Changed
outlier_ratiodefaults to 1.5 (was 2): a one-off 17 GB peak where a job
usually takes 9 GB no longer sets the need for its next 10 runs. A job with
two sizes, such as a cache hit and a full build, still reserves the big one.
Prebuilt binaries for macOS (Apple silicon, Intel) and Linux (x86_64, ARM, static musl). Each archive holds the taskguard binary; a .sha256 file sits next to it.
taskguard v0.9.0
Added
taskguard top: the Queue view is a tree. The jobs of one task-runner
run sit under one row, named by the command that started it, with their
counts, CPU and memory. The taskguard calls running inside a job, such as a
dev stack's start-up builds, show under that job with their own CPU and
memory.←folds a row,→unfolds it, andEnteron a run's row does
both. Running jobs keep their place,
oldest first.--wraps: a command that runs other taskguard jobs runs straight through
from its first run, and the jobs inside it queue on their own. A queued run
that had jobs inside it prints advice to pass it.- The Config view of
taskguard toplistswarn_admit.
Changed
- A command that wraps other taskguard jobs, such as
taskguard bun run ci:localaround a turbo run, runs straight through from its second run on,
and the jobs inside it queue on their own. Queued as one job, it booked its
biggest run, up to 20 GB, also when turbo's cache left almost nothing to do;
on one machine such wrappers waited 995 minutes in 17 days. A long-lived
job, a job in a pool, and a job with a minimum stay queued.
taskguard prune 'KEY' --applyqueues the command again. (#24) - A run starved of memory raises the next run's need by 25% only when it
reached 90% of its need. One starved far below its need was squeezed by
other programs, and the extra room went unused. (#24) - macOS: the kernel's memory warning level no longer stops every new job.
At warning, jobs start while memory fits without any leeway, and not while
a first run still grows (at most two minutes). A dev stack kept the level
at warning with half of memory free, and the browser proof that needed the
stack waited behind it for up to an hour. Critical still stops every new
job.warn_admit = falsebrings back the old rule. (#22)
Prebuilt binaries for macOS (Apple silicon, Intel) and Linux (x86_64, ARM, static musl). Each archive holds the taskguard binary; a .sha256 file sits next to it.
taskguard v0.8.0
Added
- Partial fit: a job that cannot fit now, because its CPU need is above the
whole limit or because it has waited pastmax_bypass, starts on part of
its CPU need. It needs at leastpartial_fit(0.5) of its need free, and
the machine near its lowest CPU use of the last two minutes, so it starts
at a quiet moment. It then books the cores it got, not its whole need, so
other jobs keep moving. Memory stays a hard rule.partial_fit = 0turns
it off. Set it per machine in~/.config/taskguard/config.toml. - A waiting job that CPU keeps out, and that cannot fit now (its need is
above the whole limit, or it has waited pastmax_bypass), prints advice
lines in its wait output: what it needs against the limit, what holds the
room and how long that should last, how many jobs wait behind it, how long
it has waited, when partial fit starts it, the command to start it now and
what that costs, and the settings that change the outcome. They print with
hints off too;--quiethides them. taskguard start JOBstarts a waiting job now, whatever the limits say, as
gintaskguard topdoes. JOB is a pid, a ticket, a key, or a unique
part of a key. A job that CPU keeps out and starts this way (or at a
--sttimeout) books only the cores that were free, so the jobs behind it
keep moving.startis now a subcommand: to run a program namedstart,
usetaskguard run -- start.
Changed
- Backfill is on by default:
max_backfillis 600 (10 minutes) instead of
0. Small jobs that fit pass a reserved job that cannot start yet. After 10
minutes only jobs that end before the reserved job could start still pass,
so the machine drains for it, and it starts at the latest once no other job
runs, also when its need is above the whole limit. The same holds for a
waiting job with a higher priority: for its first 10 minutes, jobs with a
lower priority that fit start while it cannot. Setmax_backfill = 0for
the old behavior.
Fixed
- The CPU need is learned from the cores a job used, not from the time its
threads waited for a core. Tools that start one thread per core (tsgo,
oxlint) waited on every core of a busy machine, so they learned a need of
the whole machine and blocked the queue. Recent runs count more: a run
counts half as much as one four runs newer. A run far above the others is
an outlier and counts byoutlier_weights, as memory peaks do. A
CPU-starved run no longer raises the need to what it waited for; starved
runs are left out while three runs that were not starved remain. A running
job books the cores it uses, and the--min-cpuadvice after a starved run
pins the learned need. Runtaskguard prune KEY --applyon a job whose need
is still inflated to start again from its next runs.
Prebuilt binaries for macOS (Apple silicon, Intel) and Linux (x86_64, ARM, static musl). Each archive holds the taskguard binary; a .sha256 file sits next to it.
taskguard v0.7.1
Fixed
- Steady long-lived jobs reserve recent CPU use plus a margin instead of
runnable wait, and current memory plus 25% instead of a historical floor.
Historical CPU needs are clamped to the host's logical core count.
Prebuilt binaries for macOS (Apple silicon, Intel) and Linux (x86_64, ARM, static musl). Each archive holds the taskguard binary; a .sha256 file sits next to it.
taskguard v0.7.0
Added
- One run that takes far more memory than the job's other runs no longer sets
its memory need at once. A peak more thanoutlier_ratio(2) times the next
peak below it, and at leastoutlier_min(1 GB) above it, is an outlier.
It counts byoutlier_weights: the first time not at all, the second time
half, the third time in full. A run that failed never counts. Jobs whose
usual peak is underoutlier_minhave no outliers. The job prints a warning
after an outlier run, with the reason, and the reason is kept in the history. taskguard outliers [PATTERN] [--ratio R] [--min SIZE]lists the outliers,
and how much each one counts.taskguard prunedrops runs from the history: of the keys that match a
pattern, older than an age (--older-than 30d), or the outliers
(--outliers). Without--applyit only shows what it would do. Pruned
runs are kept aside, andprune --undoputs them back.- A job short of memory by at most
noise_mem(2%) of RAM, or of CPU by at
mostnoise_cpu(0.5) cores, starts anyway. So does a job that only
programs outside taskguard keep out (outside_admit, on by default), while
memory stays underpause_at. Each such start prints a warning with what
the job lacks. - Help for each command:
taskguard help COMMAND, orCOMMAND --help.
taskguard help --allprints every command in full in one text, for LLM
agents. doctor --explainshows a command's outliers.taskguard tophas a Tasks view (tab 4): every task taskguard knows, with
its namespace, runs, last run and learned needs. Filter by namespace (n),
text (/) and age (a: not run for 1, 7, 30 or 90 days).xprunes every
run of the selected task,Xevery task the filters leave. In a job's view,
xprunes the selected run. Both ask first, andtaskguard prune --undo
puts the runs back.
Changed
- In
taskguard top,?lists every key of the view you are in, and the keys
of every view. The bottom line starts with? keysand shows only the keys
of the view that fit, so it no longer runs off the screen. - The tabs of
taskguard topafter Runs move one up: Trends is 5, Warnings 6,
Namespaces 7, Config 8, Help 9. - A memory need never passes
mem_max, as a CPU need never passescpu_max.
A job that once took more is capped there, and says so. Before, it could
only start on an empty machine.
Prebuilt binaries for macOS (Apple silicon, Intel) and Linux (x86_64, ARM, static musl). Each archive holds the taskguard binary; a .sha256 file sits next to it.
taskguard v0.6.0
Prebuilt binaries for macOS (Apple silicon, Intel) and Linux (x86_64, ARM, static musl). Each archive holds the taskguard binary; a .sha256 file sits next to it.
taskguard v0.5.0
Prebuilt binaries for macOS (Apple silicon, Intel) and Linux (x86_64, ARM, static musl). Each archive holds the taskguard binary; a .sha256 file sits next to it.
taskguard v0.4.4
Prebuilt binaries for macOS (Apple silicon, Intel) and Linux (x86_64, ARM, static musl). Each archive holds the taskguard binary; a .sha256 file sits next to it.