Skip to content

feat(dashboard): live CPU/mem stats stream (spec 16) - #104

Merged
gustavobertoi merged 1 commit into
mainfrom
feat/dashboard-stats
Jul 1, 2026
Merged

feat(dashboard): live CPU/mem stats stream (spec 16)#104
gustavobertoi merged 1 commit into
mainfrom
feat/dashboard-stats

Conversation

@gustavobertoi

Copy link
Copy Markdown
Contributor

What

Implements the reserved dashboard live CPU/mem stats feature (spec 16 §CPU/mem, §gotchas [Q-DASH-STATS]) — the --no-stats flag was previously a no-op.

internal/docker

  • New read-only ContainerStats(ctx, id) (Stats, error) on the Client interface + moby impl. Uses a one-shot ContainerStats with IncludePreviousSample so CPU% is computed from the cpu/precpu delta in a single call (no streaming reader held open — the dashboard fetches per visible container each poll).
  • Decoded into a small Stats{CPUPercent, MemUsage, MemLimit, MemPercent}.
  • cpuPercent applies the Docker formula (cpuDelta/systemDelta) × onlineCPUs × 100, guarded against counter resets and the first (no-previous) sample. Memory is cache-adjusted (inactive_file/total_inactive_file subtracted) to match docker stats.
  • Mirrored in MockClient with a StatsCalls counter + StatsErr.

dashboard (internal/cli)

  • dashRow carries HasStats/CPUPercent/MemUsage/MemLimit; the table gains CPU% (engine) and MEM columns (labeled engine per spec — VM-skewed on Desktop/WSL2), rendered when no sample was taken. Detail pane shows cpu/mem too.
  • collectDashboardData does a bounded, read-only stats fetch per visible container on the existing poll and folds each sample into the matching row (keyed by the row's display name: shared DNS alias / <project>/<service>).
  • --no-stats now actually disables the fetch (zero ContainerStats calls); stats default on for the cockpit. The non-TTY --json snapshot includes a stats section when enabled and available.

Why

Closes the reserved stats slice of spec 16, making the shared-services graph's live resource usage observable in the cockpit while keeping a cheap opt-out for low-power machines.

Read-only invariant

No flock taken, only SDK reads — consistent with the dashboard's "observe, never mutate" contract.

How tested

CGO_ENABLED=0 go build ./..., CGO_ENABLED=1 go test ./internal/... (+ -race on docker/cli), gofmt -l clean, go vet ./... clean. New table-driven tests:

  • CPU% from a stats delta (resets, first-sample, idle, multi-CPU, online-CPU fallback) + full statsFromResponse projection + cache-adjusted memory.
  • Mock ContainerStats feeding the collector and the Bubble Tea model (Update/View render the CPU%/MEM columns without a TTY).
  • --no-stats issues zero ContainerStats calls.

🤖 Generated with Claude Code

Implement the reserved dashboard CPU/mem stats feature (spec 16 §CPU/mem,
§gotchas [Q-DASH-STATS]).

docker:
- Add a read-only ContainerStats method to the Client interface + moby impl:
  a one-shot ContainerStats with IncludePreviousSample so CPU% is computed
  from the cpu/precpu delta in a single call (no streaming reader held open).
  Decoded to a small Stats{CPUPercent, MemUsage, MemLimit, MemPercent}.
- cpuPercent applies the Docker formula (cpuDelta/systemDelta × onlineCPUs),
  guarded against counter resets / the first sample. Memory is cache-adjusted
  (inactive_file subtracted) to match `docker stats`.
- Mirror ContainerStats in MockClient with a StatsCalls counter + StatsErr.

dashboard:
- dashRow carries HasStats/CPUPercent/MemUsage/MemLimit; the table gains
  "CPU% (engine)" and MEM columns (labeled engine per spec: VM-skewed on
  Desktop/WSL2), rendered "—" when no sample was taken; detail pane shows both.
- collectDashboardData fetches a bounded, read-only sample per visible
  container each poll and folds it into the matching row (keyed by the row's
  display name: shared alias / "<project>/<service>").
- --no-stats now actually disables the fetch (zero ContainerStats calls);
  stats default ON for the cockpit. The --json snapshot includes a "stats"
  section when enabled and available.

Stays strictly read-only: no flock, only SDK reads. Tests: table-driven CPU%
computation + full projection, cache-adjusted memory, the mock feeding the
collector/model, --no-stats skipping the fetch, and the CPU%/MEM columns
rendering via Update/View without a TTY.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gustavobertoi
gustavobertoi merged commit 90f8ee3 into main Jul 1, 2026
4 checks passed
@gustavobertoi
gustavobertoi deleted the feat/dashboard-stats branch July 1, 2026 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant