atagen

roundabout

public
Star0
Watch0
9117440444eb609ecea8476b5a62b03917077591
1
ssh://brewery@pls.lobotomise.me:33/atagen/roundabout.git

Blame: README.md

93b435e669a5atagen2026-09-27 03:59 UTC1
# roundabout
93b435e669a5atagen2026-09-27 03:59 UTC2
93b435e669a5atagen2026-09-27 03:59 UTC3
five agents, one machine, one youtube video. roundabout makes the builds take turns.
93b435e669a5atagen2026-09-27 03:59 UTC4
93b435e669a5atagen2026-09-27 03:59 UTC5
agents say roughly how big a build is. a tiny daemon, started on demand and gone once idle, lets it run when there's room. two heavy builds never overlap, nothing gets promised disk that isn't there, and small stuff keeps slipping through while the big one grinds. roundabout doesn't run anything itself, it just holds the door.
93b435e669a5atagen2026-09-27 03:59 UTC6
93b435e669a5atagen2026-09-27 03:59 UTC7
## use
93b435e669a5atagen2026-09-27 03:59 UTC8
fdad58f8133aatagen2026-10-25 23:14 UTC9
the cli is `roundabout`, short enough that agents don't mind typing it
93b435e669a5atagen2026-09-27 03:59 UTC10
93b435e669a5atagen2026-09-27 03:59 UTC11
```
fdad58f8133aatagen2026-10-25 23:14 UTC12
roundabout run --size m -- cargo build
fdad58f8133aatagen2026-10-25 23:14 UTC13
roundabout run --ram 6G --cores 8 --time 10m -- nix build .#thing
fdad58f8133aatagen2026-10-25 23:14 UTC14
roundabout status
fdad58f8133aatagen2026-10-25 23:14 UTC15
roundabout suggest -- cargo build --release
fdad58f8133aatagen2026-10-25 23:14 UTC16
roundabout expect --size l   # building soon, save me a seat
fdad58f8133aatagen2026-10-25 23:14 UTC17
roundabout watch 7        # exits when lease 7 is done or overdue
fdad58f8133aatagen2026-10-25 23:14 UTC18
roundabout kill 7
fdad58f8133aatagen2026-10-25 23:14 UTC19
roundabout extend 7 10m
fdad58f8133aatagen2026-10-25 23:14 UTC20
roundabout artifacts check | list | gc [--cap] [--project] | pin <path>
93b435e669a5atagen2026-09-27 03:59 UTC21
```
93b435e669a5atagen2026-09-27 03:59 UTC22
93b435e669a5atagen2026-09-27 03:59 UTC23
`run` exits with the build's code, 124 if `--deadline` killed it, 75 if `--max-wait` ran out.
93b435e669a5atagen2026-09-27 03:59 UTC24
caac091f257aatagen2026-10-04 11:41 UTC25
a granted build gets `CARGO_BUILD_JOBS`, `MAKEFLAGS`, `CMAKE_BUILD_PARALLEL_LEVEL` and a `NIX_CONFIG` with `cores`/`max-jobs` to match its grant.
caac091f257aatagen2026-10-04 11:41 UTC26
fdad58f8133aatagen2026-10-25 23:14 UTC27
on linux, `roundabout` creates a transient scope through the systemd user manager's D-Bus `StartTransientUnit` method. the command waits until its scope is active, then runs with its original environment and terminal I/O. `CPUQuotaPerSecUSec` caps CPU time at the granted core count, and `MemoryMax` caps the whole build at its granted RAM multiplied by `overrun.ram_slack`, with at least the granted RAM available. these limits cover descendants too, even when a tool ignores the job-count environment variables. linux requires a running systemd user manager and user bus, a failed scope refuses the build rather than running it without limits. other platforms retain the job-count environment variables without cgroup enforcement.
ecc1cf8df0f5atagen2026-09-27 13:36 UTC28
fdad58f8133aatagen2026-10-25 23:14 UTC29
the grant can be bigger than the ask. idle cores go to whoever's building, minus what other sessions have said they'll want via `roundabout expect`. spare cores never hold up admission, oversubscribing cpu only slows things down, but each one is charged ram at the build's own ram per core since more jobs eat more memory. a session's build counts as an `expect` too, so agents that forget still leave a mark. `--exact` opts out.
93b435e669a5atagen2026-09-27 03:59 UTC30
93b435e669a5atagen2026-09-27 03:59 UTC31
## claude code
93b435e669a5atagen2026-09-27 03:59 UTC32
93b435e669a5atagen2026-09-27 03:59 UTC33
```
93b435e669a5atagen2026-09-27 03:59 UTC34
nix build .#plugin
93b435e669a5atagen2026-09-27 03:59 UTC35
claude plugin marketplace add ./result
93b435e669a5atagen2026-09-27 03:59 UTC36
claude plugin install roundabout@roundabout
93b435e669a5atagen2026-09-27 03:59 UTC37
```
93b435e669a5atagen2026-09-27 03:59 UTC38
052a1a49371catagen2026-09-27 03:59 UTC39
or on nixos
052a1a49371catagen2026-09-27 03:59 UTC40
052a1a49371catagen2026-09-27 03:59 UTC41
```nix
052a1a49371catagen2026-09-27 03:59 UTC42
imports = [ roundabout.nixosModules.default ];
052a1a49371catagen2026-09-27 03:59 UTC43
programs.roundabout.enable = true;
052a1a49371catagen2026-09-27 03:59 UTC44
programs.roundabout.settings.budget.ram = "16GiB";   # lands in /etc/roundabout/config.toml
052a1a49371catagen2026-09-27 03:59 UTC45
```
052a1a49371catagen2026-09-27 03:59 UTC46
052a1a49371catagen2026-09-27 03:59 UTC47
that installs roundabout and a socket-activated systemd user unit for the daemon (`socketActivation = false` to let clients spawn it instead). `programs.roundabout.claude` has read-only `hooks`, `skill` and `plugin` for whatever writes your claude config, e.g. `settings.hooks = config.programs.roundabout.claude.hooks`.
052a1a49371catagen2026-09-27 03:59 UTC48
fdad58f8133aatagen2026-10-25 23:14 UTC49
the plugin brings a skill and hooks. unwrapped builds get bounced back with instructions, overdue builds get mentioned to the agent that started them, and agents over their artifact cap get told to tidy up before they stop. `roundabout` needs to be on PATH for the agent too, so install `.#roundabout` alongside.
93b435e669a5atagen2026-09-27 03:59 UTC50
4e5ca2e20401atagen2026-09-28 11:42 UTC51
## codex
4e5ca2e20401atagen2026-09-28 11:42 UTC52
4e5ca2e20401atagen2026-09-28 11:42 UTC53
```
4e5ca2e20401atagen2026-09-28 11:42 UTC54
nix build .#codex-plugin
4e5ca2e20401atagen2026-09-28 11:42 UTC55
codex plugin marketplace add ./result
4e5ca2e20401atagen2026-09-28 11:42 UTC56
codex plugin add roundabout@roundabout
4e5ca2e20401atagen2026-09-28 11:42 UTC57
```
4e5ca2e20401atagen2026-09-28 11:42 UTC58
fdad58f8133aatagen2026-10-25 23:14 UTC59
restart codex and review the plugin hooks with `/hooks` before using them. install `.#roundabout` as well so `roundabout run` and `roundabout expect` are on PATH. codex can also load the hooks directly from `~/.codex/hooks.json` or a trusted project's `.codex/hooks.json` using the `hooks` object in `result/plugins/roundabout/hooks/hooks.json`. use one installation route to avoid duplicate reminders.
4e5ca2e20401atagen2026-09-28 11:42 UTC60
fdad58f8133aatagen2026-10-25 23:14 UTC61
on nixos, `programs.roundabout.codex` exposes read-only `hooks`, `skill` and `plugin`, alongside the claude options. the codex hooks deny unwrapped builds and suggest `roundabout run` instead of silently rewriting the command, which keeps the normal tool approval flow intact even when `enforce.unwrapped` is set to `wrap`. `roundabout run` and `roundabout expect` associate builds with the current codex session, and session-end hooks release its expectation.
4e5ca2e20401atagen2026-09-28 11:42 UTC62
4e5ca2e20401atagen2026-09-28 11:42 UTC63
## omp
4e5ca2e20401atagen2026-09-28 11:42 UTC64
4e5ca2e20401atagen2026-09-28 11:42 UTC65
```
4e5ca2e20401atagen2026-09-28 11:42 UTC66
nix build .#omp-plugin
4e5ca2e20401atagen2026-09-28 11:42 UTC67
omp plugin link ./result
4e5ca2e20401atagen2026-09-28 11:42 UTC68
```
4e5ca2e20401atagen2026-09-28 11:42 UTC69
fdad58f8133aatagen2026-10-25 23:14 UTC70
restart omp after linking. install `.#roundabout` on PATH for the agent's bash tool, or set `ROUNDABOUT_CLI` to the `roundabout` binary for the extension. the plugin bundles the skill and checks shell builds before they run. it also delivers overdue reminders, checks artifact caps before the main agent stops, and releases expectations when a session closes or switches. wrapped builds and `roundabout expect` inherit the omp session id through the bash tool's environment. on nixos, `programs.roundabout.omp` exposes read-only `plugin` and `skill` options.
4e5ca2e20401atagen2026-09-28 11:42 UTC71
93b435e669a5atagen2026-09-27 03:59 UTC72
## config
93b435e669a5atagen2026-09-27 03:59 UTC73
56a853ebf254atagen2026-09-27 03:59 UTC74
`~/.config/roundabout/config.toml`, falling back to `/etc/roundabout/config.toml`, or `$ROUNDABOUT_CONFIG` over both. the daemon picks up edits on save, on linux anyway. a broken config gets complained about and ignored rather than taking every agent down with it. everything's optional, these are the defaults
93b435e669a5atagen2026-09-27 03:59 UTC75
9117440444ebatagen2026-10-26 02:44 UTC76
project settings can live in a hidden `.resrc` file. each client command starts in its current directory and walks upward until it reaches the git root. the first `.resrc` it finds wins, so a nested file takes precedence over one at the repository root. directories outside a git worktree do not load a project file. `.resrc` uses the same TOML settings, and omitted values keep the user or system settings
9117440444ebatagen2026-10-26 02:44 UTC77
9117440444ebatagen2026-10-26 02:44 UTC78
```toml
9117440444ebatagen2026-10-26 02:44 UTC79
# .resrc
9117440444ebatagen2026-10-26 02:44 UTC80
[budget]
9117440444ebatagen2026-10-26 02:44 UTC81
ram = "16GiB"
9117440444ebatagen2026-10-26 02:44 UTC82
cores = 8
9117440444ebatagen2026-10-26 02:44 UTC83
9117440444ebatagen2026-10-26 02:44 UTC84
[artifacts]
9117440444ebatagen2026-10-26 02:44 UTC85
project_cap = "20GiB"
9117440444ebatagen2026-10-26 02:44 UTC86
```
9117440444ebatagen2026-10-26 02:44 UTC87
93b435e669a5atagen2026-09-27 03:59 UTC88
```toml
93b435e669a5atagen2026-09-27 03:59 UTC89
[budget]
93b435e669a5atagen2026-09-27 03:59 UTC90
ram = "75%"            # of total, or "24GiB"
93b435e669a5atagen2026-09-27 03:59 UTC91
ram_reserve = "4GiB"   # MemAvailable never drops below this on our account
93b435e669a5atagen2026-09-27 03:59 UTC92
cores = "100%"
93b435e669a5atagen2026-09-27 03:59 UTC93
disk_reserve = "20GiB" # per fs, capped at a tenth of it
93b435e669a5atagen2026-09-27 03:59 UTC94
disk_paths = ["/nix/store"]
93b435e669a5atagen2026-09-27 03:59 UTC95
psi_max = 40.0         # memory psi avg10 above this holds heavies back
93b435e669a5atagen2026-09-27 03:59 UTC96
93b435e669a5atagen2026-09-27 03:59 UTC97
[heavy]                # any one of these makes a build heavy
93b435e669a5atagen2026-09-27 03:59 UTC98
ram = "4GiB"
93b435e669a5atagen2026-09-27 03:59 UTC99
cores = "50%"
93b435e669a5atagen2026-09-27 03:59 UTC100
disk = "5GiB"
93b435e669a5atagen2026-09-27 03:59 UTC101
93b435e669a5atagen2026-09-27 03:59 UTC102
[sizes.m]              # s m l xl built in, add your own
93b435e669a5atagen2026-09-27 03:59 UTC103
ram = "2GiB"
93b435e669a5atagen2026-09-27 03:59 UTC104
disk = "1GiB"
93b435e669a5atagen2026-09-27 03:59 UTC105
cores = 4
93b435e669a5atagen2026-09-27 03:59 UTC106
time = "5m"
93b435e669a5atagen2026-09-27 03:59 UTC107
93b435e669a5atagen2026-09-27 03:59 UTC108
[queue]
93b435e669a5atagen2026-09-27 03:59 UTC109
linger = "5s"
93b435e669a5atagen2026-09-27 03:59 UTC110
backfill_max_time = "2m"
93b435e669a5atagen2026-09-27 03:59 UTC111
# max_wait = "30m"
ecc1cf8df0f5atagen2026-09-27 13:36 UTC112
elastic = true         # hand idle cores to whoever's building
fdad58f8133aatagen2026-10-25 23:14 UTC113
intent_ttl = "20m"     # how long a `roundabout expect` or a build keeps its claim
93b435e669a5atagen2026-09-27 03:59 UTC114
93b435e669a5atagen2026-09-27 03:59 UTC115
[overrun]
caac091f257aatagen2026-10-04 11:41 UTC116
ram = "warn"           # or "throttle", MemoryHigh at granted RAM on linux
93b435e669a5atagen2026-09-27 03:59 UTC117
ram_slack = 1.25
93b435e669a5atagen2026-09-27 03:59 UTC118
time_grace = 1.5       # overdue at this multiple of --time
93b435e669a5atagen2026-09-27 03:59 UTC119
remind_every = "5m"
93b435e669a5atagen2026-09-27 03:59 UTC120
93b435e669a5atagen2026-09-27 03:59 UTC121
[enforce]
93b435e669a5atagen2026-09-27 03:59 UTC122
unwrapped = "deny"     # "wrap" guesses a size for them, "allow" looks away
93b435e669a5atagen2026-09-27 03:59 UTC123
default_size = "m"
93b435e669a5atagen2026-09-27 03:59 UTC124
93b435e669a5atagen2026-09-27 03:59 UTC125
[artifacts]
93b435e669a5atagen2026-09-27 03:59 UTC126
project_cap = "10GiB"
93b435e669a5atagen2026-09-27 03:59 UTC127
global_cap = "60GiB"
93b435e669a5atagen2026-09-27 03:59 UTC128
idle_after = "24h"
93b435e669a5atagen2026-09-27 03:59 UTC129
size_ttl = "10m"
93b435e669a5atagen2026-09-27 03:59 UTC130
over_cap = "warn"      # "block" refuses new builds until cleaned
93b435e669a5atagen2026-09-27 03:59 UTC131
stop = "nudge"         # "block" "warn" "off"
93b435e669a5atagen2026-09-27 03:59 UTC132
nix_gc = false
93b435e669a5atagen2026-09-27 03:59 UTC133
nix_min_free = "30GiB"
93b435e669a5atagen2026-09-27 03:59 UTC134
# extra_rules = [{ name = "zig-cache", marker = "build.zig" }]
93b435e669a5atagen2026-09-27 03:59 UTC135
93b435e669a5atagen2026-09-27 03:59 UTC136
[nix]
93b435e669a5atagen2026-09-27 03:59 UTC137
inject = true
93b435e669a5atagen2026-09-27 03:59 UTC138
cores_per_job = 4
93b435e669a5atagen2026-09-27 03:59 UTC139
93b435e669a5atagen2026-09-27 03:59 UTC140
[detect]
93b435e669a5atagen2026-09-27 03:59 UTC141
# extra = ["just build"]
93b435e669a5atagen2026-09-27 03:59 UTC142
```
93b435e669a5atagen2026-09-27 03:59 UTC143
93b435e669a5atagen2026-09-27 03:59 UTC144
## license
93b435e669a5atagen2026-09-27 03:59 UTC145
93b435e669a5atagen2026-09-27 03:59 UTC146
EUPL-1.2