Skip to content

fix(cli): keep report --json stdout valid when --html/--csv are also set - #33

Merged
sturlese merged 1 commit into
mainfrom
fix/bughunt-json-stdout-clean
Jul 23, 2026
Merged

sturlese merged 1 commit into
mainfrom
fix/bughunt-json-stdout-clean

Conversation

@sturlese

Copy link
Copy Markdown
Owner

Bug

--json is documented as machine output "for pipelines," but combining it with --html/--csv corrupts the JSON. The ✓ dashboard: <path> / ✓ finance statement: <path> confirmations were printed to the same stdout, after the JSON document, so:

flightdeck report --json --html dash.html > data.json

wrote a file that fails to parse (json.JSONDecodeError: Extra data).

Root cause

In the report command the html/csv success messages went unconditionally to the stdout console, the same console console.print_json writes to — with no guard for as_json.

Fix

Route the two confirmations to err (stderr, already defined in the module) when --json is set, so stdout carries only the JSON. Without --json they stay on stdout next to the human-readable report — non-pipeline output is unchanged. The files are still written either way.

Test

Adds test_report_json_with_html_keeps_stdout_valid_json. It runs the real CLI in a subprocess because the in-process CliRunner does not separate rich's stderr console — only genuine OS streams show the fix. Asserts stdout parses as JSON, the confirmation lands on stderr, and both files were written. Verified it fails on main (Extra data) and passes on the branch.

Full local gate green: 204 passed, coverage 93.17% (≥85%), ruff clean, offline demo + audit verify intact.

🤖 Found and fixed by an autonomous bughunt iteration.

…o set

`--json` is documented as the machine-readable output "for pipelines" and
prints the report to stdout. But when --html and/or --csv were passed too, the
"✓ dashboard: <path>" / "✓ finance statement: <path>" confirmations were
printed to the SAME stdout, appended after the JSON document — so the natural
combination `flightdeck report --json --html dash.html > data.json` (get the
machine JSON AND persist the dashboard) wrote a file that no longer parses as
JSON ("Extra data").

Route the two write confirmations to stderr when --json is set (the module
already has `err = Console(stderr=True)`), so stdout carries only the JSON
document. Without --json they stay on stdout next to the human-readable report,
so the non-pipeline output is unchanged.

The regression test runs the real CLI in a subprocess: the in-process
CliRunner does not separate rich's stderr console, so only genuine OS streams
demonstrate the fix (stdout parses as JSON; the confirmation lands on stderr).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sturlese

Copy link
Copy Markdown
Owner Author

Adversarial gate: PASS. An independent opus reviewer attacked from five angles (non-json regression, incomplete fix, subprocess-test soundness, the rich-stderr fd assumption, and exit-code/encoding) and could not refute it (high confidence).

  • Non-json path untouched: notify = console when --json is absent, so report --csv still writes "finance statement" to stdout — the existing CliRunner CSV test still passes.
  • Only stdout write in the json path is print_json: terminal.render is in the else branch, _org errors only to stderr, and the metrics/backlog helpers print nothing. Verified --json --html --csv yields byte-perfect JSON on stdout with both confirmations on stderr; --json alone yields valid JSON and 0 bytes on stderr.
  • Real fd 2 confirmed: running the CLI in a subprocess with stdout/stderr redirected to separate files shows fd 1 = clean parseable JSON, fd 2 = the two ✓ confirmations. The subprocess regression test is sound and CI-portable.

Full local gate green: 204 passed, coverage 93.17%, ruff clean, demo + audit verify intact.

@sturlese
sturlese merged commit 05dbb67 into main Jul 23, 2026
5 checks passed
@sturlese
sturlese deleted the fix/bughunt-json-stdout-clean branch July 23, 2026 16:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant