Gunnar

nordisk/dwarves

.nornir/agent-pool-design.md

<!-- nornir:class: design -->
# agent pool — design

> **Class:** `design` · **Function axis:** `f0` the fleet-as-data surface (the whole) · `f1` the `AgentPool` RPC · `f2` the `dwarves agents` verb (fat + thin) · `f3` the jera reuse seam
> **Strict:** this file states *only what the agent-pool surface IS* — its parts and why.
> Sibling how-to: [agent-pool-guide.md](agent-pool-guide.md); zero-knowledge walkthrough: [agent-pool-for-idiots.md](agent-pool-for-idiots.md).

The **agent pool** surface exposes the worker fleet the forge dispatches to — every
registered agent (id, group, capabilities, cores/mem, liveness) — **as data**, over
the wire and from the CLI. It closes the D1-wishlist gap "`AgentPool` … remain open"
and is the enabling wire for the parity §4b **"Agents view — the live fleet."** Before
this, `Show`/`History` carried pipelines + runs but the fleet was invisible over gRPC.

![agent-pool flow](assets/agent-pool.svg)

## The system as one — the function map (f0)

| part | lives in | what it is |
|---|---|---|
| `f1` `AgentPool` RPC | [`crates/dwarves/src/server.rs`](../crates/dwarves/src/server.rs) · [`proto/dwarf.proto`](../crates/dwarves/proto/dwarf.proto) | `DwarfSvc::agent_pool` reads the shared jera `Scheduler` → `AgentPoolReply{ repeated AgentRow }` |
| `f2` `dwarves agents` verb | [`crates/dwarves/src/main.rs`](../crates/dwarves/src/main.rs) | fat = the local single-machine pool; thin = the server's live pool over `AgentPool` |
| `f3` reuse seam | jera (edda) | `Scheduler::state_json()` — the ONE fleet-as-data contract, consumed not twinned |

## Why it is shaped this way

- **Reuse jera's fleet contract (Law #1).** dwarves owns no scheduler of its own —
  the `Agent`/`Scheduler`/worker-group machinery lives in `jera::agent` (edda). The RPC
  handler reads `Scheduler::state_json()` (the documented `{agents:[{id,group,caps,
  cores,mem_mb,state}]}` shape jera already ships for exactly this "feed a UI Agents
  pane" purpose) and maps each row onto the wire — no internal accessor, no twin fleet
  model. The fat CLI reads the same `state_json()` from the local default pool, so fat
  and thin print an identical listing.
- **One surface, single→multi machine.** The pool is `LocalAgent` single-machine
  today (`scheduler_with_local_default` seeds `local-0` in group `default`, cap
  `cargo`). A `GrpcAgent` fleet registers into the SAME `Scheduler`, so `AgentPool`
  reports a real multi-machine fleet with **no change to this surface** — the lazy-but-
  seam-honest discipline the parity spec mandates for distribution.
- **Additive + gated like every RPC.** The `AgentPool` RPC + `thin_agents` client
  compile only under `--features server` (proto codegen + tonic); the fat `dwarves
  agents` needs only `agent_bridge` (always compiled). nornir's lean path-dep pulls
  nothing new.
- **Observed as data (L4).** The handler is drive-and-assert tested (a fresh server's
  pool is exactly `local-0`/`default`/`cargo`/idle, RED-when-broken) with a
  `dwarves::server / agent_pool` testmatrix cell. No new UI surface ⇒ no robot row
  (the fleet-as-data contract is the assertion, same as the `Badge` RPC).

## Place in the nordisk constellation

This is a **dwarves-owned** control-plane surface reading a **shared jera** primitive.
It mirrors the constellation's "expose the fleet as data so a robot/matrix can assert
it" law: nornir, dwarves and holger all get multi-machine awareness from the one jera
crate, and each surfaces its pool the same way. The eventual Agents pane
(`facett-jobview`-adjacent) consumes this RPC behind the same `state_json` shape.

## Deferred

- **No Agents UI pane yet.** The RPC + CLI land the wire; mounting a live Agents pane
  in `dwarves-gui` (over this RPC or a fixture) is a follow-up, mirroring the
  Status-board build-out.
- **Single in-process agent.** Only the default `LocalAgent` is registered today; the
  `GrpcAgent` remote-registration path (a `<host> dwarves agent --group …` joining the
  server's scheduler over gRPC) is the tracked multi-machine leg — this surface already
  reports whatever the scheduler holds, so it needs no change when that lands.
- PDFs (`docs/*.pdf`) left for the Loki bencher's `nornir docs book` render.