Relationships
#3356 Fleet visibility: live worker host map grouped by label, with slot-level lease state
Opened by swamp_lord · 10/13/2026· Shipped 10/13/2026
Problem
Operators running worker fleets against swamp serve can't see fleet health or placement capacity at a glance. The dashboard's Workers panel (packages/dashboard/src/views/System.tsx) is a flat three-column table: worker name, status, active dispatch count. WorkerInfo carries only name, status and activeDispatchIds.
Most of what an operator needs is already tracked but never reaches the UI:
WorkerStateSchema(src/domain/models/worker/worker_model.ts):labels,tokenName,platform/arch,capacity,activeDispatchIds,enrolledAt,lastSeenAt,disconnectedAt.- The gateway stamps
labels.fleet = <token name>at enrollment unless the worker sets its own (src/serve/worker_gateway.ts). So a "fleet" is a label value, and labels are the same selectors steps use for placement (labels:/platform:). - Each dispatch on a worker holds a step lease (
step_lease_model.ts):active, thencompleted/failed/expired, withworkflowName/jobName/stepName/methodName.
Disconnected workers also vanish from view, so a fleet that just went down is hard to diagnose.
Proposed design
A live host map (D3), modelled on Datadog's host map but built from swamp's worker and lease model. It uses the dashboard's existing tokens and fonts (Plus Jakarta Sans / JetBrains Mono, --running / --info / --warning / --danger, --success for completed leases) in both light and dark themes.
Groups
- Workers are grouped by a chosen key: fleet (default), any label key (
region,tier,gpu, …), or platform/arch. Workers without the key go into an(unlabeled)group. - Groups are laid out as a squarified treemap. Each group's area is proportional to its workers' total slot capacity, so large fleets dominate the canvas and small ones get a corner.
- Each group card shows its name,
up/total up · used/total slots, and a thin status-mix bar. - Changing the group key re-flows the treemap and animates workers into their new groups.
Workers
- Each worker is a square sized by area to its
capacity, so an 8-slot runner is 8× the area of a 1-slot worker. - Inside a group, workers settle as a loose cluster. They are hard-clamped to the card by their own size and never overlap the header or the edges.
- Worker status sets the square's color:
unverified= dashed--infooutline (enrolling/verifying),idle= neutral,busy=--runningwith a subtle pulse,draining=--warning,disconnected=--danger.
Dispatch slots
- Each square is divided into one cell per slot (√capacity grid). Slots are allocated independently and each shows its own lease state: free (hollow), lease
active(solid), then a brief flash ofcompleted/failed/expiredbefore the slot frees. busy/idlefollows from the slots: a worker is busy when at least one lease is active.- A draining worker takes no new leases and disconnects once its leases end. A worker that drops mid-run shows its active leases as
expired.
Lifecycle motion
- New workers fade and grow in place inside their group. They don't fly in from outside the card.
- A replacement enrolled under the same token appears where the previous one left.
- Workers leaving the map shrink and fade in place.
Visibility TTL
- Disconnected workers stay on the map, fading and shrinking, until
disconnectedAt + TTL, then they leave. - The TTL is adjustable in the toolbar (30-minute steps, default 2h).
Placement selector
- A free-text selector (
gpu=h100, region=us-west-2, plusplatform=/arch=) behaves like a step'slabels:/platform:block. Non-matching workers dim, and the header shows how many workers match and how many free slots they have.
Filters and detail
- Status toggle chips filter the map.
- Clicking a worker opens a panel with its token, status, time in state, platform/arch, labels, and a per-slot list (workflow › step ·
model.method, lease age or outcome). - A worker table gives a non-visual fallback.
Mockup (simulated data): the "Fleet Pulse" Claude artifact; link available on request.
What implementation needs
- Expose the extra worker fields to the dashboard (
labels,tokenName,platform,arch,capacity,activeDispatchIds,enrolledAt,lastSeenAt,disconnectedAt). TodayWorkerInfoonly hasname/status/activeDispatchIds. - Expose the step leases for each worker's
activeDispatchIds(state, workflow/job/step/method,createdAt/endedAt), plus recently ended leases so outcomes can flash. - Keep disconnected workers visible until
disconnectedAt + TTLinstead of dropping them. - Push updates to the dashboard so the map animates rather than polling a full snapshot.
Open questions
- Pending dispatches (
pending_dispatch_model.ts, statewaiting) aren't in any slot yet. Show them as a queue badge per group, or next to the placement selector? - Should the TTL be a dashboard-only view setting, or a serve config value?
- Expected fleet size: the cluster layout is comfortable up to a few hundred workers. Beyond that, should groups collapse into summary tiles?
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.