Skip to content

feat(cli): migrate fingerprint and separation onto the renderer - #82

Merged
copyleftdev merged 2 commits into
mainfrom
feat/render-migrate-batch3
Jul 30, 2026
Merged

feat(cli): migrate fingerprint and separation onto the renderer#82
copyleftdev merged 2 commits into
mainfrom
feat/render-migrate-batch3

Conversation

@copyleftdev

@copyleftdev copyleftdev commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Refs #75. Third batch — Markdown coverage now 6 of 12 commands.

What these two added

fingerprint exercises Fields (hash summary) + Table (repeated motifs) + a conditional Note, and both its streaming and non-streaming paths are migrated. The streaming path gains an explicit note that the Merkle shape hash needs the whole tree in memory — previously that was only implied by a bare (not available in streaming mode).

separation was the best test of note fidelity, because its caveats are the product: rank by MI not strength, SEP only for ordered fields, and precision-at-prevalence being far below corpus precision. All three survive as blockquotes:

## Best single rule, priced at the assumed prevalence
| FEATURE | TPR | FPR | PRECISION | RULE |
|---|---|---|---|---|
| $.pkg_name | 0.0324 | 0.0000 | 1.00000 | == "@0xobelisk/sui-cli" |

> Precision here is what the rule would deliver in a population with the
> assumed prevalence — usually far below its corpus precision.

The migration deleted code, not just moved it

truncate_path — a manual fixed-width JSONPath trimmer separation needed because it formatted its own columns — became dead once the renderer computed widths. Removed rather than left behind.

A regression an existing test caught

The Fields block rendered Nodes 25 where the hand-written output had Nodes: 25. fingerprint_min_nodes.rs asserted on "Nodes:" and failed.

I fixed the renderer rather than relaxing the test: Fields now emits the trailing colon, so text output stays byte-compatible with anything grepping it. That's also the argument for those tests asserting on text shape and not only on JSON — a formatting change is a user-visible change.

Bookkeeping

Tripwire moves fingerprintcluster. The migrated-command loop now covers four commands, so RENDERS_MARKDOWN still can't claim more than is true.

Full workspace suite: 79 test binaries, 0 failures. cargo fmt --check clean. 0 clippy diagnostics.

Remaining

cluster, drift, batch, cascade, score, governance, core-team, compare.

Summary by CodeRabbit

  • New Features

    • Added full Markdown output support for the fingerprint and separation commands.
    • Improved structured output for these commands, including clearer tables, headings, notes, and conditional result details.
    • Added Markdown rendering for fingerprint results in both streaming and standard output modes.
  • Documentation

    • Updated CLI format documentation to identify fingerprint and separation as supporting real Markdown output.
  • Style

    • Improved text report readability by adding colons to field labels.

Third batch. Markdown coverage is now 6 of 12 commands.

`fingerprint` exercises the Fields block (hash summary) plus a table
(repeated motifs) plus a conditional note, and both its streaming and
non-streaming paths are migrated. The streaming path gains an explicit note
that the Merkle shape hash needs the whole tree in memory, which was
previously implied by a bare "(not available in streaming mode)".

`separation` was the most useful test of note fidelity, since its caveats are
the point of the command: rank by MI not strength, SEP only for ordered
fields, and precision at prevalence being far below corpus precision. All
three survive both formats as blockquotes.

Two things the migration deleted rather than moved:

- `truncate_path`, a manual fixed-width JSONPath trimmer `separation` needed
  because it was formatting its own columns. The renderer computes widths, so
  the helper became dead code and is gone.
- The per-command format match arms, which is the point.

One regression caught by an existing test and fixed rather than papered over:
the Fields block rendered `Nodes  25` where the hand-written output had
`Nodes:       25`. `fingerprint_min_nodes.rs` asserted on `"Nodes:"` and
failed. Rather than relax the test, Fields now emits the trailing colon, so
text output stays byte-compatible with what anything grepping it expects.
That is also why the tests assert on text shape and not only on JSON.

Test bookkeeping: the tripwire moves from `fingerprint` to `cluster`, and the
migrated-command loop now covers four commands.

Refs #75. Remaining: cluster, drift, batch, cascade, score, governance,
core-team, compare.
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@copyleftdev, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: efdfedfb-d512-4118-8026-4d38ef5d1c92

📥 Commits

Reviewing files that changed from the base of the PR and between e66bf47 and bd3e87c.

📒 Files selected for processing (1)
  • vajra-cli/tests/format_honesty.rs
📝 Walkthrough

Walkthrough

Fingerprint and separation non-JSON output now uses structured reports and tables with Markdown support. Renderer coverage documentation, format-honesty tests, and text field-label formatting were updated accordingly.

Changes

Markdown renderer migration

Layer / File(s) Summary
Renderer coverage and formatting contract
vajra-cli/src/main.rs, vajra-cli/src/render.rs, vajra-cli/tests/format_honesty.rs, docs/src/commands.md
Markdown coverage now includes fingerprint and separation; format-honesty tests identify cluster as unmigrated, and text field labels include trailing colons.
Fingerprint structured rendering
vajra-cli/src/main.rs
Streaming and non-streaming fingerprint output use structured reports, conditional fields, motif tables, notes, and Markdown-or-text rendering.
Separation structured rendering
vajra-cli/src/main.rs
Separation output uses report metadata, feature-separation tables, ranking notes, and an optional best-rule table. best single rule table when applicable.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main CLI change: migrating fingerprint and separation onto the renderer.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/render-migrate-batch3

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@vajra-cli/src/main.rs`:
- Around line 2169-2183: The report-generation paths retain unbounded rows and
rendered strings; replace the repeated-motif flow around report.table and the
output flow around class, feature, and rule reporting with bounded or streaming
sinks. For aligned text, use a two-pass or spooled approach so output is emitted
incrementally without retaining the complete report; update both
vajra-cli/src/main.rs:2169-2183 and vajra-cli/src/main.rs:2849-2934, preserving
Markdown and text formatting.

In `@vajra-cli/tests/format_honesty.rs`:
- Line 180: Extend the Markdown-honesty coverage loop in the relevant test to
exercise the separation command alongside the existing commands. Provide
separation’s labelled fixture and required CLI arguments, or add an equivalent
dedicated integration test, ensuring the renderer-backed separation output is
validated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c6756b60-375f-4fd1-87b5-c8067fb255f8

📥 Commits

Reviewing files that changed from the base of the PR and between f9f499a and e66bf47.

📒 Files selected for processing (4)
  • docs/src/commands.md
  • vajra-cli/src/main.rs
  • vajra-cli/src/render.rs
  • vajra-cli/tests/format_honesty.rs

Comment thread vajra-cli/src/main.rs
Comment on lines +2169 to +2183
let mut t =
render::Table::new(&["HASH", "COUNT"], "no repeated subtree shapes found");
for m in &output.repeated_motifs {
t.push(vec![m.hash.clone(), m.count.to_string()]);
}
report.table(t);
}

print!(
"{}",
match cli.format {
Format::Markdown => report.to_markdown(),
_ => report.to_text(),
}
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Avoid buffering unbounded rendered reports.

Both paths retain all rows in render::Table and then allocate the complete rendered output string. Fingerprint has no motif bound, and separation --top-k 0 emits every feature.

  • vajra-cli/src/main.rs#L2169-L2183: render repeated motifs through a bounded/streaming sink.
  • vajra-cli/src/main.rs#L2849-L2934: stream class, feature, and rule output; use a two-pass/spooled strategy if aligned text requires widths.

As per coding guidelines, “Every algorithm must support arbitrary input scale through streaming and bounded-memory designs; non-streaming algorithms do not ship.”

📍 Affects 1 file
  • vajra-cli/src/main.rs#L2169-L2183 (this comment)
  • vajra-cli/src/main.rs#L2849-L2934
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@vajra-cli/src/main.rs` around lines 2169 - 2183, The report-generation paths
retain unbounded rows and rendered strings; replace the repeated-motif flow
around report.table and the output flow around class, feature, and rule
reporting with bounded or streaming sinks. For aligned text, use a two-pass or
spooled approach so output is emitted incrementally without retaining the
complete report; update both vajra-cli/src/main.rs:2169-2183 and
vajra-cli/src/main.rs:2849-2934, preserving Markdown and text formatting.

Source: Coding guidelines

Comment thread vajra-cli/tests/format_honesty.rs
Added separation to RENDERS_MARKDOWN in the previous commit without adding
it to the coverage assertions — exactly the rot that loop exists to prevent.
It needs a labelled fixture and its own flags so it cannot join the simple
loop, hence a dedicated test, which also asserts the caveat notes survive as
blockquotes since those are the point of the command.

Raised by CodeRabbit on #82.
@copyleftdev

Copy link
Copy Markdown
Owner Author

Both findings valid; one fixed, one folded into the tracked streaming work.

separation missing from the coverage loop — fixed. This was exactly the rot the loop exists to prevent: I added separation to RENDERS_MARKDOWN without asserting it actually renders Markdown. It needs a labelled fixture and its own flags so it can't join the simple loop, so it gets a dedicated test — which also asserts the caveat notes survive as blockquotes, since those are the point of that command.

Unbounded buffering — real, but it is the render-side face of #75's streaming half, not a regression here. Worth being precise about what changed:

  • Before: cmd_anomalies built a String with writeln! and printed it; separation collected shown into a Vec and built a JSON Vec<Value>. Both already buffered everything.
  • After: rows accumulate in render::Table and render to one String. Same order of memory, one indirection more.

So this migration neither introduced nor worsened it — but you're right that it's now concentrated in one place, which is actually the useful part: a streaming renderer is one change here rather than twelve at the call sites. Report would need to become a sink that writes blocks as they're pushed, with Table streaming rows and column widths either pre-declared or computed in a first pass.

That belongs with the streaming work already tracked on #75, because the same decision governs it: separation --top-k 0 can't bound its output without bounding the analysis that produced it. Fixing the renderer alone would just move the buffer up a level.

I'd rather leave it as a documented characteristic of the current design than half-stream it. Noted on #75 so it isn't lost.

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