Skip to content

Unified trace system for CLI and TUI observability#213

Merged
jeremy merged 4 commits intomainfrom
devtools
Mar 8, 2026
Merged

Unified trace system for CLI and TUI observability#213
jeremy merged 4 commits intomainfrom
devtools

Conversation

@jeremy
Copy link
Member

@jeremy jeremy commented Mar 8, 2026

Summary

  • Add BASECAMP_TRACE env var with structured, slog-backed JSON tracing to file — replaces ad-hoc BASECAMP_DEBUG (backward compat preserved for 1, 2, true)
  • TraceCategories bitmask (http, tui, all) controls what gets logged; trace files land in the CLI's resolved cache directory (respects --cache-dir and profiles)
  • SDK hooks log structured HTTP events (operation start/end, request start/end, retry) independent of stderr verbosity
  • basecamp tui --trace enables all-category tracing; 12 instrumentation sites in the workspace (key presses, navigation, sidebar, account switching, relayout, overlays)
  • Bubbletea debug output goes to a separate .debug.log file to keep the JSON trace parseable
  • bin/devtools script launches tmux with TUI + tail -f in a split pane for live trace viewing

Test plan

  • make fmt-check && make vet && make test && make test-e2e all pass
  • go vet ./... clean
  • Pre-existing lint issue in picker.go confirmed unrelated
  • BASECAMP_TRACE=http basecamp projects produces structured JSON in ~/.cache/basecamp/trace-{pid}.log
  • BASECAMP_TRACE=all basecamp tui produces both HTTP and TUI events
  • BASECAMP_DEBUG=1 basecamp projects backward compat maps to TraceHTTP
  • BASECAMP_DEBUG=yes basecamp projects does NOT create a trace file (tightened)
  • basecamp tui --trace prints trace path, enables all categories
  • BASECAMP_TRACE=http basecamp tui --trace widens to all categories
  • bin/devtools opens tmux split with TUI + tail (requires tmux)
  • bin/devtools 'https://3.basecamp.com/...' URL arg preserved without shell mangling

Copilot AI review requested due to automatic review settings March 8, 2026 19:46
@jeremy jeremy requested a review from a team as a code owner March 8, 2026 19:46
@github-actions github-actions bot added commands CLI command implementations tui Terminal UI tests Tests (unit and e2e) enhancement New feature or request labels Mar 8, 2026
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 9 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="internal/appctx/context.go">

<violation number="1" location="internal/appctx/context.go:211">
P1: `a.Hooks.SetTracer(t)` will panic if `a.Hooks` is nil. The lines just above already guard `a.Hooks` with a nil check before calling `SetLevel` — this call should follow the same pattern.</violation>
</file>

<file name="internal/commands/tui.go">

<violation number="1" location="internal/commands/tui.go:53">
P1: Missing nil guard on `app.Hooks` before calling `SetTracer`. The code below already checks `app.Hooks != nil` before `SetLevel(0)`, but this call site does not, risking a nil-pointer panic when `--trace` is passed.</violation>
</file>

<file name="internal/observability/tracer_test.go">

<violation number="1" location="internal/observability/tracer_test.go:19">
P3: Double-close on the tracer: `defer tr.Close()` and the explicit `tr.Close()` both run, but `Close()` doesn't nil-out `file` after closing, so the deferred call closes an already-closed fd. Remove the defer since the explicit close is needed before `ReadFile`.</violation>
</file>

<file name="internal/observability/tracer.go">

<violation number="1" location="internal/observability/tracer.go:73">
P2: The `cats` field is read/written without synchronization. `EnableCategories` performs `t.cats |= cats` while `Log` and `Enabled` read `t.cats`, which is a data race under the Go memory model. Consider using `atomic.Int32` (or `atomic.Int64`) for `cats` to make the read/write race-free, especially since the struct is designed for cross-goroutine use (nil-safe, slog-backed).</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a unified trace system for CLI and TUI observability. It adds a new observability.Tracer type backed by slog JSON output to a file, controlled via the BASECAMP_TRACE environment variable (with backward compatibility for BASECAMP_DEBUG). The TUI gains a --trace flag and 12 instrumentation sites, while SDK hooks log structured HTTP events to the same trace file. A bin/devtools script facilitates live trace viewing via tmux.

Changes:

  • New observability.Tracer with bitmask-based category filtering (http, tui, all), nil-safe methods, env var parsing, and comprehensive tests
  • Integration of the tracer into CLIHooks (structured HTTP event logging), App lifecycle (init in ApplyFlags, cleanup in Close via PersistentPostRunE), and TUI workspace (12 trace call sites for key presses, navigation, sidebar, overlays, etc.)
  • New bin/devtools bash script and --trace flag on the tui command for developer ergonomics

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
internal/observability/tracer.go New Tracer type with slog JSON handler, category bitmask, file management, env var parsing, and path utilities
internal/observability/tracer_test.go Comprehensive tests for Tracer creation, categories, nil-safety, env var parsing, and backward compat
internal/observability/hooks.go Adds tracer field to CLIHooks with SetTracer method; all hook methods now also log to the file-based tracer
internal/appctx/context.go Adds Tracer field to App, initializes tracer in ApplyFlags, adds Close() for resource cleanup
internal/cli/root.go Adds PersistentPostRunE to call app.Close() on exit
internal/commands/tui.go Adds --trace flag, tracer setup/widening, bubbletea debug log separation, and workspace option wiring
internal/tui/workspace/workspace.go Adds Option pattern with WithTracer, trace() helper, and 12 trace instrumentation sites
bin/devtools Bash script for tmux-based live trace viewing during TUI development
.surface Registers new --trace flag in API surface

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

jeremy added 3 commits March 8, 2026 13:47
Replace ad-hoc BASECAMP_DEBUG with BASECAMP_TRACE — a structured, slog-backed
JSON tracing system. Per-process file naming supports concurrent sessions.

- TraceCategories bitmask (TraceHTTP, TraceTUI, TraceAll) with EnableCategories
- ParseTraceEnv/ParseTraceEnvWithCacheDir reads env, BASECAMP_DEBUG backcompat
  tightened to only accept values the verbosity parser recognises (numeric, "true")
- TracePath(cacheDir) respects CLI's resolved cache directory and profile scoping
- CLIHooks gains SetTracer(); each hook method logs structured JSON independent
  of stderr verbosity level
- App.Tracer field initialized in ApplyFlags; App.Close() for resource cleanup
- PersistentPostRunE on root command calls App.Close()
…acing

- Workspace gains Option/WithTracer pattern; 12 trace sites covering key presses,
  navigation, sidebar, account switching, relayout, overlays
- `basecamp tui --trace` enables TraceAll (or widens an env-created tracer);
  bubbletea debug log writes to separate .debug.log to keep JSON trace parseable
- bin/devtools launches tmux with TUI + tail -f in a split pane; uses direct
  argv form (no sh -c) to avoid shell metacharacter issues with URLs/paths
… warning

- Guard a.Hooks and app.Hooks with nil checks before SetTracer calls
  (context.go, tui.go) for consistency with existing SetLevel guards
- Use atomic.Int32 for Tracer.cats field so EnableCategories is safe to
  call concurrently with Log/Enabled from other goroutines
- Remove defer/explicit double-close in tracer tests; use single explicit
  close before ReadFile
- Log warning to stderr when NewTracer fails in --trace flag path
Copilot AI review requested due to automatic review settings March 8, 2026 20:54
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Include "true" in ParseTraceEnv doc comment for TraceAll values
- Switch ParseTraceEnv tests to use TempDir to avoid leaking files
- Add TestCLIHooks_TracerIntegration verifying SetTracer + hooks write
  structured trace events to the log file
@jeremy jeremy merged commit 06e0e5f into main Mar 8, 2026
25 checks passed
@jeremy jeremy deleted the devtools branch March 8, 2026 22:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands CLI command implementations enhancement New feature or request tests Tests (unit and e2e) tui Terminal UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants