Skip to content

1.0 polish: structured output, edge cases, docs accuracy (tracking) #29

Closed

Description

@quinnmil

Tracking issue for smaller findings from a pre-1.0 audit of the whole CLI against a live account. None of these are individually severe enough for their own issue, but they're worth fixing before or shortly after calling the CLI stable. Two more substantial bugs and a design decision were split into their own issues: #26 (traces show duration units), #27 (setup framework links), #28 (drop TOON support).

Check items off as they're fixed; feel free to split any of these into their own PR/issue if a fix turns out bigger than expected.

Structured (--json) output consistency

  • -n/--limit is silently ignored whenever --json is active, across every command — applyLimit runs after the command has already decided to emit structured output. This is the one path where limiting matters most for scripts/agents.
  • insights list / insights show never call applyLimit at all, even in human table output — -n has no effect on either.
  • scout usage --billing-period --json doesn't include the billing period or the server-reported total; structured output is identical in shape to a plain --from/--to run, so a script can't tell what window it got or what the billed figure is.
  • jobs metrics --type latency --json includes a total sub-series that's actually execution time, not latency. The human chart correctly special-cases this and plots the right series — the structured output should carry the same fix (or at least a clearer key name).

Command correctness / edge cases

  • scout usage --app <id> (without --by-day) silently ignores --app and returns all apps instead of just that one.
  • scout anomalies --endpoint silently returns empty results for the base64 form the README documents; it actually expects the plain endpoint name shown in anomalies list. Inconsistent with every other --endpoint flag in the CLI (which take base64), and returning nothing instead of an error is the worst version of this.
  • --endpoint/--job flags accept an empty string and pass cobra's required-flag check, producing a raw server error instead of a clean client-side "value required" message.
  • A malformed --job value, or a bare job class name without its queue (a very plausible copy-paste from jobs list's output, which shows Job and Queue as separate columns), surfaces a raw API error (500) instead of a clean client-side error. resolveJobID only base64-encodes when the value contains a / and never validates the result.
  • scout auth logout errors instead of no-op'ing on a machine with no config directory yet (config.Clear() has no MkdirAll) — hits any fresh install, or a logout right after a failed login.
  • --from/--to accept a reversed range (--to earlier than --from) and extreme values (very large durations, negative durations) with no validation — results are silently empty or nonsensical rather than a clear error.
  • --app 0 or a negative app id produces "no app specified — use --app flag..." even though --app was passed; requireAppID only treats values > 0 as "specified."
  • scout traces show <id> on a background job trace id 404s with no hint about why — a user who just copied a valid-looking id out of traces list --job has no signal that job traces have no detail endpoint (this is documented in --help and the README, just not in the error itself).
  • scout errors show <id> always renders Count: 0 even when errors list shows a nonzero count for the same group — looks like an upstream API field, but the CLI renders it unconditionally with no guard.
  • scout insights show field order is nondeterministic (ranges over a Go map with no sort) and leaks raw Go formatting (map[...], <nil>) for nested fields instead of formatting them for display.

Chart rendering

  • Chart downsampling picks a point by index rather than aggregating a bucket (max/mean), so a real spike or dip can be completely invisible in the plotted line while the Min/Max stat line above it is correct.
  • Chart min/max/avg are computed over the full series including a trailing partial time bucket, which can drag the reported range toward zero even when the "real" data doesn't include a zero.
  • Large millisecond values render unit strings like "kms" instead of promoting to seconds (e.g. "5.7s" rather than "5.7kms").
  • The chart title is rendered twice — once as a styled header, once via the underlying chart library's own caption.
  • RenderChart hides the Summary: field entirely when the value is exactly 0, inconsistent with every other stat on the same line.

Docs & help text accuracy

  • README documents the credentials path as ~/.config/scout-apm/config.json. On macOS, os.UserConfigDir() (which internal/config uses) actually resolves to ~/Library/Application Support/scout-apm/config.json. Two README sections affected (Authentication, Configuration) — document both platforms, or print config.Path() somewhere discoverable.
  • Root command's --help long description hasn't kept pace with the CLI's growth — it doesn't mention anomalies, billing, or usage, even though the README tagline does.
  • --from flag help text lists example units but omits 2w, which both the README and the flag's own parse-error message reference.
  • --endpoint flag descriptions ("URL-encoded endpoint ID/name") don't say the actual required format is Base64 URL-safe, and following the literal wording (URL-encoding the name) produces a server error. scout endpoints list also provides no direct way to get the id --endpoint needs — no field in --json output, it has to be scraped out of a link value. scout jobs got this right (job_id field, plus accepting the plain queue/JobName form); endpoints is the odd one out and could mirror it.
  • No command defines a cobra Example: block. For the commands with non-obvious id formats (jobs metrics, traces list --job), a copy-pasteable example in --help would help a lot.
  • A couple of README code examples use a hardcoded id that no longer resolves against a fresh account (a stale error-group id, for instance) — prefer phrasing like "an id from scout errors list" over a hardcoded number.

Test coverage gaps

  • resolveUsageTimeframe (the --billing-period timeframe-resolution logic) has no direct unit tests. Untested: the --from/--to mutual-exclusion branch, the "billing period not available" error path, and the future-end-date-clamped-to-now behavior. All of this is testable without a live server via httptest.
  • A latent, not-yet-reproduced boundary bug in usage --by-day (without --by-app): per-app time series are concatenated into one flat slice before being bucketed by day, so the interval calculation at the seam between two apps' data can attribute a large time gap's "transactions" to whichever app happens to end first, if one app's data ends meaningfully before another's begins within the window. Add a unit test for this concatenation boundary and a guard (bucket per-app before summing, rather than relying on the gap always producing a negative/zeroed interval in practice).

Minor polish

  • CI (.github/workflows/ci.yml) runs go test -race and golangci-lint but not gofmt -l or go vet — add both so formatting/vet issues don't linger unnoticed (there's one pre-existing unformatted file today).
  • renderUsageBar renders as a fully empty bar for any utilization below 2.5% (20 blocks × 2.5% rounds to 0 filled) — indistinguishable from true zero usage. Consider a 1-block floor whenever usage is nonzero.
  • Chart/metric titles print a raw identifier (a base64 endpoint id, or "App Add usage command #6") where a decoded/friendly name is available and would read better.
  • scout apps show <id> returns fewer fields than the same app's entry in scout apps list (the API's single-app payload omits last_reported_at) — consider falling back to the list data for that field.
  • A couple of error paths duplicate the same text twice (an HTTP status name followed by an identical response body).
  • A couple of string-truncation spots slice by byte rather than by rune, which could split a multibyte character — latent today since observed data is ASCII-only.
  • scout setup <name> is case-sensitive with no suggestion of valid framework names in its error message.
  • No client-side --type validation on metrics get / endpoints metrics (round-trips to the server for what jobs metrics already validates locally with a helpful message).
  • Tables have no column width cap — a long URI or name can produce an unreadably wide table in a normal-width terminal.

Activity

  1. added
    bugSomething isn't working
    documentationImprovements or additions to documentation
    on Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdocumentationImprovements or additions to documentation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions