Agents, Agent Templates, Jobs, And Locks
Gitmoot uses runtime-neutral agent records. A named agent has a runtime, runtime reference, repo access, role, capabilities, and optional template.
Agents
Agents can be started by Gitmoot or subscribed from an existing runtime session.
gitmoot agent start project-planner --runtime codex --repo owner/repo --template planner
gitmoot agent subscribe reviewer --runtime codex --session <session-id> --repo owner/repo --capability review
Agent Templates
Agent Templates are reusable prompt/profile bundles. Gitmoot snapshots template content into each job so the job has reproducible instructions.
gitmoot agent template list
gitmoot agent template show planner
Templates are read-only installed data (#2204). Gitmoot inspects an installed
row and reads its content into a job, but it cannot author, update, or
distribute one; a template that must change is edited or re-seeded directly in
its agent_templates store row.
Jobs
Jobs are units of routed work. They can come from PR comments, local
agent ask/agent review dispatches, heartbeat schedules, pipeline stages,
retries, or merge actions. A seat's own implementation is also a job — not a
dispatched one: gitmoot job record --type implement writes the row after the
fact, which is how 568 of the last 570 implement rows arrived.
Delegations
An agent's result can return a validated delegations[] DAG, and Gitmoot
dispatches each entry as a child job. Dependency-free delegations run in
parallel, and once every top-level delegation is terminal Gitmoot enqueues a
single coordinator continuation job back to the delegating agent to synthesize
the results. Delegation trees are bounded by a depth cap, a per-root job budget, a
per-root wall-clock budget, a per-coordinator width cap, and loop detection. When
a bound trips, the offending delegations are not dropped silently — the engine
enqueues one terminal coordinator continuation that synthesizes a best-effort
result instead of recursing forever. See the
Result Contract for the full field reference.
Orchestra is gitmoot's name for this structured multi-agent delegation: a
conductor (coordinator) returns a delegations[] score, the players (child
agents) run in parallel or in dependency order, and a finale (continuation)
reconvenes and synthesizes the results. It is a naming layer on top of the same
delegations mechanics described above.
Choosing a worker: registered agent vs. ephemeral worker
There are only two kinds of worker you deliberately create — a registered agent and an ephemeral worker. A third, the temp worker, is created automatically by the daemon and is never something you make by hand.
| Registered agent | Ephemeral worker | Temp worker | |
|---|---|---|---|
| Who creates it | You (gitmoot agent start / subscribe / type set) | A coordinator (a delegations[] entry with an ephemeral spec) | The daemon, automatically |
| Persists | Yes — lives in the registry | No — auto-disposed after its job | No — auto-disposed after its job |
| Identity / name | Stable, addressable | Synthetic, hidden from the registry | Synthetic, hidden from the registry |
| Reusable | Across many jobs and PR subscriptions | One job, then gone | One job, then gone |
| Built from | Your config (runtime / model / template / capabilities) | An inline spec on the delegation | A fork of an existing registered agent |
| Purpose | A durable role | A one-off delegated task | Concurrency — run a registered agent's jobs in parallel |
A temp worker is just "this registered agent is busy; clone it to run another
job in parallel, then drop the clone." You influence it only through a managed
agent type's max_background — you never create one directly.
Create a registered agent when the role is durable and addressable:
- You will reuse it (a repo's
reviewer, aplanneryou invoke repeatedly). - It needs a stable name — PR-comment subscriptions,
gitmoot agent ask <name>, the dashboard, job history and accountability. - It carries managed state: a versioned template, a resumable runtime session, repo scope / capabilities / autonomy you tune.
- You want a pool that auto-scales under load — a managed agent type with
max_background(the daemon forks temp workers from it). - It needs to itself delegate — ephemeral workers are leaf-only and cannot return their own delegations.
Spawn an ephemeral worker (via a coordinator's delegation) when the work is one-off and disposable:
- Dynamic fan-out you do not know ahead of time ("three workers each draft an option", "a cheap gate plus a strong verifier").
- No reuse, no identity, no subscription — it exists only for that delegation.
- The coordinator wants to choose the runtime and model per task (model tiering).
- You want zero setup and zero cleanup — no pre-registration, auto-disposed, never clutters the registry.
Rule of thumb: durable and named → register an agent; one-off and coordinator-spawned → ephemeral; concurrency of a registered agent → temp workers happen for you. The trade-off: registered agents have setup cost but are reusable, addressable, trainable, and resume a warm session; ephemeral workers are zero-setup and self-cleaning but cold-spawn each time and carry no accumulated identity.
Running one agent's jobs concurrently
A single registered agent answers one foreground ask at a time —
gitmoot agent ask <name> pins one resumable runtime session, serialized by the
runtime session lock. To run the same agent on several tasks at once, dispatch
background jobs; the daemon spins extra sessions two ways:
- Temp-session forking — the default.
[parallel_sessions]ships withsame_session = "fork_temp_session"andmax_temp_sessions_per_agent = 4, so a busy registered agent's extra eligible background jobs (ask/review) fork throwaway temp workers that run in parallel (same runtime only; same-checkout work stays serialized). Zero configuration — this is the "temp worker" column in the table above. - Managed agent types —
gitmoot agent type set <type> --max-background Ndefines a reusable, addressable pool. Dispatch withgitmoot agent run <type> --type <type> --background …and the daemon reuses an idle instance or spins a new one, up toN.
--type selects this managed pool; it never selects the job action. On agent run, use the independent --action ask|review override when routing must be
explicit. For example, --type reviewer --action review --pr 42 dispatches the
review through the reviewer pool. implement is not an action Gitmoot
dispatches (#2203), so there is no fixer-pool fix pass: a changes_requested
verdict wakes the --lead seat, which implements in its own session and records
it with gitmoot job record --type implement.
Both need daemon job slots to run at the same time: max_background and temp
sessions are session slots, but the daemon still executes at most --workers
jobs at once (default 1; raise it, e.g. --workers 6).
Precedence: dispatch resolves a registered single instance by name before a
managed type, so a single agent named researcher shadows a researcher type —
force the type with --type researcher. Since v0.5.1 a foreground gitmoot agent ask <type> (the ask action) routes to the managed type synchronously
(spins/reuses an instance up to max_background); review to a type
still uses --background.
Locks
Gitmoot uses separate locks for separate resources:
- Repo checkout locks keep daemon ticks from using the same local checkout at the same time.
- Runtime session locks serialize Codex, Claude, or Kimi delivery for the same
runtime:<runtime>:<runtime_ref>across daemon jobs,job run, and synchronousagent ask. - Branch locks prevent multiple agents from racing on the same implementation branch.
Review and ask jobs usually inspect and report. Implementation jobs should only mutate branches when Gitmoot assigned the job and the branch lock is held.
Task branch locks cover the full implement, review, and fix lifecycle, so an
implement success deliberately keeps the lock. Cancelling a top-level implement
releases its lane only when no other non-terminal task or job references the
same repository and branch. The daemon also reclaims locks unchanged for at
least 24 hours, but only after no non-terminal task or job remains. Tasks in blocked,
awaiting_human_merge, or awaiting_human keep their locks for human resumption.
The daemon defaults to --workers 1. Raise workers only when jobs use
independent runtime sessions or managed agent types with max_background
greater than one.
Recovery after a crash or reboot
When a running job's worker dies, the daemon recovers it so work is never stranded. Recovery is boot-aware:
- Same boot (a daemon restart or crash where the host stayed up): a running
job that still holds a runtime session lock whose lease has not elapsed is left
running — its real timeout has not passed, and its reparented worker may still
be making progress. Only once the lease expires (or the coarse
GITMOOT_STALE_RUNNING_AFTERbackstop trips for a job holding no lease) is it requeued. This is what stops a long job (for example a multi-hour delegation) from being requeued and double-run. - After a reboot: the machine's kernel boot id changes, which is a definitive "every process from the previous boot is dead" signal. On its next startup and every tick the daemon immediately requeues any job claimed on a previous boot and reclaims its stranded runtime session lock — regardless of how long the lease still had to run — so a rebooted host resumes work at once instead of waiting the lease or the stale-running window out.
On platforms without a kernel boot id (non-Linux), the boot-aware step is a no-op and recovery falls back to the lease/age behavior above.