Skip to content

--silent still prints dashboard URL in foreground --web and replay #209

@jrob5756

Description

@jrob5756

Summary

Follow-up to #201 / #203, which fixed --silent --web-bg for both run and resume. Three other dashboard-URL prints share the same shape and still violate the --silent contract ("No progress output. Only JSON result on stdout."):

  1. src/conductor/cli/app.py:990replay command:
    console.print(f"\n[bold green]▶ Replay dashboard:[/] {dashboard.url}\n")
    console.print("[dim]Press Ctrl+C to exit[/dim]\n")
  2. src/conductor/cli/run.py:1173 — foreground run --web:
    _verbose_console.print(f"[bold cyan]Dashboard:[/bold cyan] {dashboard.url}")
  3. src/conductor/cli/run.py:1685 — foreground resume --web:
    _verbose_console.print(f"[bold cyan]Dashboard:[/bold cyan] {dashboard.url}")

Note that _verbose_console = Console(stderr=True, highlight=False) (run.py:33) is not silent-aware — it's a plain stderr Console that doesn't consult verbose_mode, despite the name suggesting otherwise.

Repro

# foreground --web
conductor --silent run examples/simple-qa.yaml --web 2> /tmp/stderr.txt
# Expected: empty (apart from the JSON result on stdout)
# Actual: 'Dashboard: http://127.0.0.1:XXXXX' on stderr

# replay
conductor --silent replay /path/to/log.jsonl 2> /tmp/stderr.txt
# Expected: empty
# Actual: '▶ Replay dashboard: http://127.0.0.1:XXXXX' and 'Press Ctrl+C to exit' on stderr

Impact

Same as #201 — stderr-only, no JSON contamination on stdout, but scripted/CI consumers that suppress stderr expecting silence will still see output bleed through.

Suggested fix

Gate each console.print (or _verbose_console.print) call by is_verbose(), matching the pattern landed in #203:

from conductor.cli.app import is_verbose

if is_verbose():
    console.print(f"[bold cyan]Dashboard:[/bold cyan] {dashboard.url}")

For the run.py sites, consider either making _verbose_console itself silent-aware (preferred, fixes the class of bug at the source — the name implies it already is) or adding explicit is_verbose() gates at the call sites. The former is cleaner but has wider blast radius and should be done carefully since _verbose_console is used throughout run.py for progress logging that's already gated elsewhere — auditing each call site is recommended.

For replay, the simple gate suffices since the URL is the primary purpose of the command (a silent caller is unusual but should still be honored).

Acceptance criteria

  • conductor --silent run <wf> --web 2>&1 >/dev/null produces no stderr from conductor (post-engine output may still occur if the workflow itself writes; that's separate).
  • conductor --silent resume <wf> --web 2>&1 >/dev/null ditto.
  • conductor --silent replay <log> produces no stderr.
  • Regression tests follow the pattern from fix(cli): suppress web-bg dashboard output in silent mode #203 (test_silent_web_bg_suppresses_dashboard_output and friends).

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:cliCLI commands and entry pointsbugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions