Skip to main content
← Back to list
01Issue
FeatureShippedSwamp CLIPublic
AssigneesNone

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, then completed / failed / expired, with workflowName / 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 --info outline (enrolling/verifying), idle = neutral, busy = --running with 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 of completed / failed / expired before the slot frees.
  • busy/idle follows 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, plus platform= / arch=) behaves like a step's labels: / 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). Today WorkerInfo only has name / 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 + TTL instead 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, state waiting) 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?
02Bog Flow
✓OPEN✓TRIAGED✓IN PROGRESS✓SHIPPEDTRIAGE+ 5 MOREREVIEW+ 24 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

10/13/2026, 9:42:23 PM

Click a lifecycle step above to view its details.

03Sludge Pulse

Sign in to post a ripple.