herdr is a terminal workspace manager built for AI coding
agents. Gas City ships a native herdr session-provider backend as an
opt-in alternative to tmux: one shared herdr session-server per city, one
workspace per rig (and one for the town), and one tab per agent. tmux stays the
default backend and the fallback — herdr is additive, selected through the same
runtime-selection setting that picks tmux, k8s, ssh, or exec.
Prerequisites
Install the herdr binary and make sure it is on PATH:
The backend is registered as a builtin runtime name (herdr) — no pack or
[runtimes.*] declaration is needed. If the binary is missing, sessions
selected onto herdr fail to start; install it before flipping the selector.
Pin a herdr version in any shared environment, and check herdr’s own release
list rather than trusting a version named here. The adapter talks to herdr’s CLI,
and minor herdr releases have changed that surface twice: 0.7.4 and 0.7.5 altered
commands the original adapter depended on, and 0.8.0 renamed the code herdr
returns for a pane with no named agent. That second one cost a fallback:
when the agent-prompt call fails on a pane with no registered agent, the adapter
pastes the text instead, and under the renamed code it stopped recognising that
case, so the original failure was returned to the caller and the paste never
happened. Nudge delivery surfaced an error rather than going quiet, but nothing
in the build or the default test suite went red, so the regression had to be
noticed in the field.
The adapter reads 0.8’s spelling now. One gap is known and tracked: 0.8.0 made
herdr’s agent registry detection-based, so the live journey that drives herdr’s
session-event stream waives itself when the installed herdr reports 0.8 or later
rather than asserting against a registry it no longer matches
(#5808). That waiver reads
the version, not the registry: a herdr whose version cannot be read or parsed
runs the journey, and on 0.8 its registry assertions then fail. The other
real-herdr journeys (provider lifecycle, pane binding, activity, and the runtime
conformance suite) do run on 0.8; the kind-launch journey needs an installed
claude binary as well and skips without one. 0.7.1 remains the version the
recorded end-to-end validation ran against.
That is the reason to pin. The tests that drive a real herdr binary are opt-in
(make test-herdr-live); the default suite runs against a fake, so a change on
herdr’s side cannot fail the build. After a herdr upgrade, run
make test-herdr-live and then put a session through a full work cycle before
trusting it.
Enabling herdr
herdr is selected with the same runtime selector used for every other
backend. That selector is city-wide ([session] provider) or, for a one-off
run, process-wide (GC_SESSION); herdr cannot be selected for individual
agents.
City default
Set the session provider in city.toml:
Every agent the city starts runs under herdr, except agents on the ACP transport
(whether pinned session = "acp" or because their provider defaults to ACP),
which route to the separate ACP backend instead (see below).
Per-agent / per-rig
herdr cannot be selected for individual agents. The runtime backend is chosen
city-wide by [session] provider (above) or process-wide by GC_SESSION
(below); no patch puts an agent onto herdr when the city default is something
else.
The per-agent patch field is session, but it selects a transport, not a
backend. It accepts only acp, tmux, or omission (IsValidSessionTransport
in internal/config/provider.go), so session = "herdr" never selects the
herdr runtime. Config validation flags it as a warning:
Under a herdr city, the transport router (internal/runtime/auto) sends only
ACP-registered sessions to the separate ACP backend and routes everything else
to the city’s base provider, which is herdr. Two consequences follow:
session = "acp" (or a provider that defaults to ACP) moves that agent off
herdr, onto the ACP backend. It is the one per-agent lever that changes which
backend an agent runs on.
session = "tmux" does not keep an agent on tmux. The herdr provider does not
implement the transport-capability check, so the pin is neither honored nor
rejected; the agent falls back to the base provider and runs on herdr. To put
an agent on tmux, the whole city (or process) must default to tmux.
Environment (one-off)
For a quick local trial without editing config, export the selector:
GC_SESSION overrides the effective provider name for that process, the same
way it selects exec:<script> or any other backend.
Piloting safely
herdr is opt-in, and the backend is a whole-city choice, so you pilot it by
scoping which city runs on herdr, not by pinning individual agents. Recommended
path:
-
Try it per-process on a scratch city. Select herdr with the environment
variable on a throwaway city, so nothing is committed and your real city is
untouched:
Every agent in that process runs under herdr, except agents on the ACP
transport (whether pinned
session = "acp" or because their provider
defaults to ACP), which still route to the separate ACP backend. Watch it
through a normal work cycle.
-
Promote to the scratch city’s default. Once the per-process trial looks
good, set the default in that city’s
city.toml and run it end to end:
-
Widen to your real city by flipping its
[session] provider to
"herdr", once the scratch city has been stable across several work cycles.
The switch is city-wide with no way to move agents over one at a time, so
keep any city you are not ready to migrate on the tmux default.
Applying and verifying
- Reload or restart the city to apply a selector change (
gc reload, or restart
the city). Agents launched after the switch run under herdr; already-running
sessions keep their current backend until they next restart.
- Confirm the effective selector with
gc config show (the [session]
provider value, plus any agent session overrides).
- Once agents are on herdr, their workspaces and tabs are visible through
herdr’s own UI (
herdr lists the per-rig/town workspaces and per-agent tabs).
What is not wired yet
The controller does not consume herdr’s session-event stream. herdr pushes
session events and the provider implements the stream
(runtime.SessionEventProvider), but the only consumer today is the provider’s
own activity reporting. The reconciler discovers session state on its periodic
pass, so a session that goes away is noticed on the next pass rather than when the
event arrives. That costs latency rather than correctness, because each pass reads
live state rather than trusting an event. The delay is bounded by the reconcile
interval only while the controller keeps up: ticks run one at a time, so a busy
controller stretches it. That is the thing to watch during a pilot.
Layout
Within the single per-city herdr session-server, gc places agents so the
workspace/tab structure mirrors the town:
- One workspace per rig, plus one for the town (mayor and town-level
agents).
- One tab per agent. Rig polecats land in their rig’s workspace as
polecat-<themed-name> tabs; the placement is display-only and does not
change agent identity.
See internal/runtime/herdr-provider-design.md in the source tree for the
provider’s design notes, capabilities, and pilot rationale. Last modified on September 13, 2026