Skip to content

[aw][code health] render_live_sessions and render_summary inconsistently omit target_console parameter #160

@microsasa

Description

@microsasa

Root Cause

All three of the "complex" public render functions in report.py accept an optional target_console: Console | None = None parameter that lets callers control where output goes:

  • render_full_summary(sessions, *, target_console) — ✅ has it
  • render_cost_view(sessions, *, since, until, target_console) — ✅ has it
  • render_session_detail(events, summary, *, target_console) — ✅ has it

But the two "simple" public render functions do not:

  • render_live_sessions(sessions) — ❌ creates its own Console() internally, no override
  • render_summary(sessions, since, until) — ❌ creates its own Console() internally, no override

Impact

1. Inconsistent API contract. All five functions are part of the same module and documented in __all__. Callers can't treat them uniformly.

2. Forces test workaround. Tests that need to capture output from these functions must monkeypatch Console itself rather than passing a pre-configured console:

# test_report.py — forced workaround
with patch("copilot_usage.report.Console", return_value=console):
    render_live_sessions(sessions)

Every other render function is tested cleanly by passing target_console=console.

3. Prevents caller-level console sharing. cli.py creates a Console() instance in _interactive_loop and passes it to render_full_summary, render_cost_view, and render_session_detail. But it can't pass the same console to render_live_sessions or render_summary — each invocation creates a fresh Console() with potentially different settings (width, color support, etc.).

Spec

Add target_console: Console | None = None as a keyword-only parameter to both functions:

def render_live_sessions(
    sessions: list[SessionSummary],
    *,
    target_console: Console | None = None,
) -> None:
    console = target_console or Console()
    ...

def render_summary(
    sessions: list[SessionSummary],
    since: datetime | None = None,
    until: datetime | None = None,
    *,
    target_console: Console | None = None,
) -> None:
    console = target_console or Console()
    ...
  • Both functions already have an internal console = Console() that just needs to be replaced with the pattern above.
  • The * separator is already used in all other target_console-accepting functions; follow the same convention.
  • No callers in cli.py currently pass target_console to these, so there are no call-site changes required (backward-compatible addition).
  • Update __all__ is not needed (no API surface change).

Testing Requirement

  • Remove the patch("copilot_usage.report.Console", ...) workaround from test_report.py's _capture_output helper and replace with a direct target_console= argument.
  • Add tests that pass an explicit target_console to both functions and assert output is captured correctly — analogous to existing tests for render_full_summary and render_cost_view.
  • Regression: all existing render tests must continue to pass.

Generated by Code Health Analysis ·

Metadata

Metadata

Assignees

No one assigned

    Labels

    awCreated by agentic workflowaw-dispatchedIssue has been dispatched to implementercode-healthCode cleanup and maintenance

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions