feat(cli): add a shared output renderer; migrate anomalies to real Markdown - #80
Conversation
…rkdown Closes the design question blocking #75's first half, and starts the migration. Commands formatted their own output per format with println!, which is exactly why `--format markdown` and `--format compact-ai` fell through to text for eleven of twelve commands: each command had to implement every format, so most implemented one. A command now builds a `render::Report` — headings, key/value fields, tables, notes, nested lists — and the renderer emits it per format. Adding a format costs one implementation rather than twelve. The design question was whether one model covers outputs ranging from flat key/value to nested detail lists. It does, with five block types, validated against the two most awkward shapes rather than the easiest: `anomalies` needs a nested list (type instabilities with their distributions) and three tables with distinct columns. `anomalies` is migrated and now emits genuine Markdown — `##` headings, real `|---|` tables, `>` blockquoted notes — rather than the text output relabelled. It also gains a summary block it did not have before, since the model made one cheap. Format tracking is now per format rather than per command, because coverage is genuinely uneven: `anomalies` renders Markdown but has no compact-AI view. A command joins RENDERS_MARKDOWN only when it actually renders Markdown, so the notice added in #79 can never claim more than is true. That makes the migration incremental and self-documenting. Renderer details worth noting: pipes are escaped and newlines flattened inside Markdown cells, so a rule like `a | b` cannot break a table; ragged rows do not panic; empty tables show their empty-state rather than an empty table; and notes survive every format, since they carry the caveats that stop a number being over-read. Tests: 10 for the renderer (both formats, markdown differs from text, pipe escaping, newline flattening, empty state, notes survive, nested lists, ragged rows, determinism) and 3 more for format tracking (per-format exactness, migrated command emits real Markdown, unmigrated stdout unchanged). Refs #75. Remaining: migrate the other ten commands, and decide whether compact-ai gets a bespoke view per command or maps to json.
|
Warning Review limit reached
Next review available in: 24 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…down Migrates score, core-team, drift and batch, completing the renderer work started in #80. Markdown is now implemented for all fifteen commands, verified by measuring each one rather than by reading match arms — the method that got the claim lists wrong three times earlier in this series. Each migration is the same shape, and each deletes more than it adds: - score_to_text becomes score_report, dropping its manual column-width computation - core-team stops calling render_core_team_text from vajra-stats, so the import goes; the function itself stays, still used by that crate's own tests - drift's five hand-rolled sections become tables, and its "rank by effect" caveat becomes a note that now explains *why* JSD and Wasserstein values are not comparable rather than just saying to prefer effect - batch's per-document stanza becomes one table, and its skipped-file list gains a note telling the reader to check the count before treating the result as complete batch also gains --redact, which it never had. That completes the privacy fix from the previous commit: all eleven commands with text output now route through maybe_redact, where six did not before. Test restructuring, because the end state changed what is true: three tests asserted that some command falls back to text for markdown. Nothing does any more, so they are removed rather than repointed at a command that does not exist. compact-ai is still a genuine fallback for eleven commands, so the notice path stays covered by a test aimed there. RENDERS_MARKDOWN is kept as an explicit list even though it now names every command. The machinery is what stops the claim drifting, and compact-ai still needs it. Closes the first half of #75. Remaining there: whether compact-ai gets a bespoke per-command view or maps to json, and the streaming question.
Refs #75 — closes the design question blocking its first half, and starts the migration.
The design question, answered
#75 asked for output to route through "the rendering system". The blocker was that no such system existed, and the design question I flagged was whether one model could cover outputs ranging from flat key/value (
fingerprint) to nested arrays with per-row severity (separation).It can, with five block types —
Heading,Fields,Table,Note,Nested. Validated against the two most awkward shapes rather than the easiest:anomaliesneeds a nested list (type instabilities with their type distributions) and three tables with different columns.Why the gap existed
Each command implemented every format itself, so most implemented one. The renderer inverts that: a command builds a
Report, and adding a format costs one implementation here rather than twelve at the call sites.anomaliesmigratedBefore,
--format markdownwas the text output verbatim. Now:Real headings, real tables, blockquoted notes. It also gains a summary block it didn't have, because the model made one cheap.
Format tracking is now per-format
Coverage is genuinely uneven —
anomaliesrenders Markdown but has no compact-AI view — so tracking splits:A command joins a list only when it actually renders that format, so the notice from #79 can never claim more than is true. That makes the migration incremental and self-documenting: each command that lands removes its own warning.
Renderer details that matter
a | b(whichseparationemits) would otherwise break the table.Tests
10 for the renderer: both formats, markdown differs from text, pipe escaping, newline flattening, empty state, notes survive, nested lists, ragged rows, determinism.
3 more for tracking: per-format exactness, migrated command emits real Markdown (asserts
##and|---|present and output differs from text), and unmigrated stdout unchanged.One test is deliberately a tripwire:
unmigrated_command_stdout_is_unchangedpoints atstatsand will fail whenstatsis migrated. That's the intended signal, and the doc comment says so.Full workspace suite: 79 test binaries, 0 failures.
cargo fmt --checkclean. 0 clippy diagnostics invajra-cliunder--all-targets --all-features.What remains on #75
compact-aigets a bespoke per-command view or simply maps tojsonfor non-essencecommands. That's a product call, not a technical one.