Skip to content

feat(logs): log streaming + Bubble Tea dashboard cockpit (spec 16) - #101

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

feat(logs): log streaming + Bubble Tea dashboard cockpit (spec 16)#101
gustavobertoi merged 1 commit into
mainfrom
feat/logs-and-dashboard

Conversation

@gustavobertoi

Copy link
Copy Markdown
Contributor

What

Implements spec 16 — the read-only log-aggregation + live TUI cockpit.

logs [service...]

The non-interactive sibling. Multiplexes container logs across the workspace's project + shared stacks via the read-only Engine SDK, filtered on devstack's own LabelManaged plus an optional per-service filter (matches shared DNS alias, bare engine, service, or owning project). Flags: --follow/-f, --tail, --since, --timestamps, --no-color, and a --json line contract {ts,service,project,stream,container,line}. A TTY gets the color-keyed service │ gutter; a pipe gets plain lines; --json implies no color. Non-follow exits at EOF; -f wires a signal-aware context so Ctrl-C tears every SDK stream down cleanly.

internal/docker streaming

Client gains ContainerLogStream(ctx, id, LogOptions) (<-chan LogLine, error):

  • Demux: non-TTY streams run through stdcopy.StdCopy (stdout/stderr split + tagged); TTY streams read raw. TTY-ness is learned via ContainerInspect (.Config.Tty, now surfaced on ContainerDetails).
  • Follow/tail/since/timestamps map onto the SDK options; the leading RFC3339 timestamp is parsed off into LogLine.TS.
  • Bounded fan-in channel for natural backpressure; unreadable logging drivers (none/syslog) return a one-line remediation, not a stack trace.
  • Mirrored in MockClient via a Streams seam so everything is testable without a daemon.

dashboard

A Bubble Tea v2 cockpit (bubbles/v2 table + lipgloss/v2) over the read-only client + ledger: shared instances (with ref counts + referencing projects) and per-project services with live state/health, a detail pane, and a log-tail pane. Keybindings q/esc/ctrl+c quit, r refreshes; a 2s safety poll keeps it live. It refuses to launch on a non-TTY (or under --json/--quiet) and instead prints a one-shot snapshot reusing the status shared + per-project projections (--json emits the machine-readable form).

Removes only the logs + dashboard stub lines from stubs.go.

Why

Spec 16: makes the shared-services graph directly observable. Pure observation over what v1 already produces — strictly read-only, no daemon, lock-free.

How tested

CGO_ENABLED=0 go build ./..., CGO_ENABLED=1 go test -race ./internal/..., gofmt -l (clean), go vet ./... (clean). New table-driven tests cover: the line emitter (split/buffer/flush/ctx-cancel), timestamp split, target label-filter resolution, the --json line shape, a full multiplex over the mock, the unreadable-driver notice, and the dashboard model's Update/View transitions + the non-TTY snapshot path. Also smoke-tested end-to-end against a live daemon (JSON + plain + --timestamps streaming, and the piped-dashboard snapshot).

🤖 Generated with Claude Code

Implements spec 16's read-only observation surface:

- `logs [service...]`: multiplexes container logs across the workspace's
  project + shared stacks via the read-only Engine SDK, filtered on the
  tool-owned label + an optional per-service filter (shared alias, bare
  engine, service, or project name). Flags --follow/-f, --tail, --since,
  --timestamps, --no-color, and a --json {ts,service,project,stream,
  container,line} line contract. TTY gets a color-keyed gutter; a pipe gets
  plain lines. Non-follow exits at EOF; -f is signal-aware for clean teardown.
- docker.Client gains ContainerLogStream: a demuxed (stdcopy for non-TTY,
  raw for TTY), optionally-following/timestamped line channel with bounded
  fan-in backpressure; TTY-ness learned via inspect; unreadable logging
  drivers surface a one-liner. Mirrored in MockClient (Streams seam).
- `dashboard`: a Bubble Tea v2 cockpit (bubbles/v2 table + lipgloss/v2)
  showing shared + project services with state/health + refs + URL and a
  log tail pane, keybindings (q/esc/ctrl+c quit, r refresh), 2s safety poll.
  Non-TTY / --json / --quiet refuses the TUI and prints a one-shot snapshot
  reusing the shared-status + per-project projections.

Removes the logs + dashboard stubs. Tests: log stream demux/emitter/ctx,
target label-filter resolution, --json line shape, multiplex over the mock,
dashboard model Update/View transitions, and the non-TTY snapshot path. No
real daemon required.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gustavobertoi
gustavobertoi merged commit 9bd2c40 into main Jul 1, 2026
4 checks passed
@gustavobertoi
gustavobertoi deleted the feat/logs-and-dashboard branch July 1, 2026 12:42
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