From ccbe0439573216d550efc8d1e0fee0310d7825d3 Mon Sep 17 00:00:00 2001 From: Wesley Johnson Date: Wed, 3 Jun 2026 01:49:27 -0500 Subject: [PATCH] fix(cli): point saved-result hint at real cost command (#538) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The saved-result "Next:" block is the canonical follow-up surface after `panel run --save`. Users reaching for per-run cost reasonably try `synthpanel cost `, which errors because `cost` only accepts the `summary` subcommand. There is no per-result `cost` command — the per-run cost rollup lives in `synthpanel report `, and cross-run aggregation lives in `synthpanel cost summary`. Surface both real paths in the hint and clarify that `report` already carries the cost rollup, so nobody guesses the nonexistent `cost ` form. Adds a regression test asserting the hint only suggests real commands. Closes #538 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/synth_panel/cli/commands.py | 5 ++++- tests/test_results_discovery.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/synth_panel/cli/commands.py b/src/synth_panel/cli/commands.py index 9cb9710..cda931f 100644 --- a/src/synth_panel/cli/commands.py +++ b/src/synth_panel/cli/commands.py @@ -6167,9 +6167,12 @@ def _print_saved_result_hint(result_id: str) -> None: """ print(f"Result saved: {result_id}", file=sys.stderr) print("Next:", file=sys.stderr) - print(f" synthpanel report {result_id} # full Markdown report", file=sys.stderr) + print(f" synthpanel report {result_id} # full Markdown report (incl. cost rollup)", file=sys.stderr) print(f" synthpanel results show {result_id} # raw saved result", file=sys.stderr) print(" synthpanel results list # all saved results", file=sys.stderr) + # #538: there is no per-result `cost ` command — the per-run cost + # rollup lives in `report` above. `cost summary` aggregates across runs. + print(" synthpanel cost summary # cost rollup across saved runs", file=sys.stderr) def handle_runs_prune(args: argparse.Namespace, fmt: OutputFormat) -> int: diff --git a/tests/test_results_discovery.py b/tests/test_results_discovery.py index a5134d0..d66e2df 100644 --- a/tests/test_results_discovery.py +++ b/tests/test_results_discovery.py @@ -140,3 +140,22 @@ def test_empty_runs_list_hints_results_store(self, capsys, tmp_path): out = capsys.readouterr().out assert code == 0 assert "synthpanel results list" in out + + +class TestSavedResultHintCommands: + """The saved-result "Next:" block must only suggest real commands (#538).""" + + def test_hint_suggests_real_commands_not_per_result_cost(self, capsys): + from synth_panel.cli.commands import _print_saved_result_hint + + _print_saved_result_hint("result-abc123") + err = capsys.readouterr().err + + # Real, existing commands are surfaced. + assert "synthpanel report result-abc123" in err + assert "synthpanel results show result-abc123" in err + assert "synthpanel results list" in err + # The per-run cost rollup is reachable via the real `cost summary` + # subcommand, not a nonexistent per-result `cost ` command. + assert "synthpanel cost summary" in err + assert "cost result-abc123" not in err