Skip to content

Long-running command stderr notices are buffered until the command returns #6218

Description

@devantler

🤖 Generated by the Daily AI Engineer

Problem

Long-running / interactive CLI commands print progress and instruction notices to
stderr while they run, but those notices don't reach the user until the command
returns. The error-handler executor (pkg/cli/ui/errorhandler/executor.go) redirects
stderr into a buffer for the duration of cmd.Execute() and only flushes it afterward,
so a command that writes a notice and then blocks shows that notice too late to be
useful.

Affected commands (each writes to ErrOrStderr() then blocks):

  • ksail workload watch"watching: <dir>" / "press Ctrl+C to stop" (workload/watch.go:197-203)
  • ksail workload intercept"intercepting inbound traffic … press Ctrl-C to stop" (workload/intercept.go:345-350)
  • ksail open web — its browser-launch fallback notice

Evidence

Flagged in review of #6216 (Codex P2, "Stream stderr before long-running commands
return"). Reproduced against the real binary: running ksail workload watch and
capturing stderr while it is still running shows watching: … / press Ctrl+C to stop only because those are written to the process stderr — but with the current
buffer-until-return design they would be withheld until the watch loop exits (i.e. after
Ctrl+C), which is exactly when the "press Ctrl+C to stop" hint is no longer useful.

Why this wasn't fixed in #6216

#6216 fixes the custom-exit-code half of the same warning-discard class (its finding 1).
The streaming half was deferred there on the premise that live streaming conflicts with
the executor's capture-for-normalization design (a naive tee would double-print the error
on failures). That premise doesn't hold once the capture is removed rather than tee'd
(see below), so this is tracked here as a focused follow-up rather than dropped.

Proposed approach (verified)

Remove the executor's stderr capture entirely:

  • Set cmd.SilenceErrors = true so Cobra no longer prints its own error line — the
    executor owns error reporting and normalizes the returned error directly. Every
    real ksail command sets SilenceUsage, so Cobra's err.Error() already carries the
    full message; the capture was redundant.
  • Commands then write warnings/notices straight to the real stderr, live, on every
    exit path (success, real error, custom-exit).

This avoids the double-print the #6216 thread worried about (there is no tee of Cobra's
error line, because Cobra no longer prints it). Verified locally against the real binary:
workload watch notices stream live, the error path prints a single normalized message
(no duplicate), and the unknown-command message is unchanged for real commands (the
synthetic-test usage hint moves to Cobra's usage output, gated by SilenceUsage).

Acceptance criteria

  • Notices written to stderr by a long-running command reach the user during
    execution, not after it returns (RED/GREEN test).
  • Error normalization and custom-exit-code (DriftExitError) behavior preserved.
  • go test ./pkg/cli/... . green; golangci-lint run clean; real-binary user-eval.

Rough size: S (executor-only change + test updates). Land after #6216 merges to
avoid a branch collision on executor.go.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status
    ✅ Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions