Skip to content

Refine NSys timeline visualization#7

Merged
lucifer1004 merged 1 commit into
mainfrom
vis-refine
Jun 11, 2026
Merged

Refine NSys timeline visualization#7
lucifer1004 merged 1 commit into
mainfrom
vis-refine

Conversation

@lucifer1004

@lucifer1004 lucifer1004 commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Default-on timeline density rendering with per-track density bins, density tooltips, preserved highlights, and controls to tune or disable density.
    • New CLI options to set density bin width and to disable density; table and CLI outputs now report density and omitted-item counters.
  • Documentation

    • New end-to-end NSys timeline example report and expanded guidance on reading density markers, placement metadata, and interpretation of truncation/omitted counts.
  • Bug Fixes

    • SVG class token sanitization fixed.
  • Chores

    • Release bumped to 0.4.0.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds density-bin aggregation and related metrics to timeline SVG rendering, updates query/response types and fingerprints, introduces CLI flags and validation, expands unit tests, and updates docs, RFCs, examples, and version/release metadata to ship the wire-version and density rollout.

Changes

Timeline density rendering and contract alignment

Layer / File(s) Summary
Data model and summary fields
crates/veloq-vis/src/model.rs, crates/nsys/veloq-nsys-query/src/viz_timeline/types.rs
Adds VizAggregation::DensityBins, VizRenderPolicy.density_bin_px, removes VizLabelPolicy.max_chars, and extends SvgRenderSummary/VizTimelineFigureRow with density and omission counters.
SVG renderer core (selection & emit)
crates/veloq-vis/src/render/mod.rs
Implements render_svg with density-aware selection pipeline (candidate clipping, density threshold selection via binary search, partition into explicit items vs density bins, deterministic explicit-item limiting), lane/layout scaling, and populated render summary.
Painter, style, and text helpers
crates/veloq-vis/src/render/painter.rs, style.rs, text.rs
Adds DensityBinDraw/push_density_bin, updates tick/label drawing to use clipPaths and style constants, adds density tooltips and density_opacity helper, and switches fit logic to width-estimation-based truncation.
NSys query, fingerprint, CLI, dispatch, and views
crates/nsys/veloq-nsys-query/*, crates/nsys/veloq-nsys/src/{cli.rs,commands/mod.rs,error.rs,help.rs,views/basic.rs}
Bumps NSys source VERSION to "v3", updates request_fingerprint inputs (drop max_chars, include density fields), extends render-policy echo and figure row with density fields, adds --density-bin-px and --no-density flags, validates density input, maps aggregation modes, adds InvalidRenderOption error and help blurb updates, and extends tabular view columns and smoke tests.
Rendering tests and wire-format smoke
crates/veloq-vis/src/render/tests.rs, crates/nsys/veloq-nsys-query/tests/wire_format_smoke.rs
Expands test coverage for density aggregation, per-track isolation, highlight preservation, density disablement and item-limit fallback, threshold tuning, annotation-specific behavior, and updates canonical wire-format allow-list to include new density fields.
Docs, examples, RFCs, work-items, and release metadata
README.md, .agents/skills/nsys-profile-analysis/*, docs/*, examples/reports/nsys-timeline-vis/*, gov/*, CHANGELOG.md, typos.toml, AGENTS.md
Documents density semantics, updates envelope examples to source.version = "v3", expands cookbook and limitations docs for new fields, publishes end-to-end example report/evidence with three figures, strengthens RFC viz timeline command-surface requirements, adds work-item and release entries, and updates file-exclusions for report SVGs.
Manifests and plugin metadata
.claude-plugin/*, .codex-plugin/*, Cargo.toml
Bumps plugin manifests and workspace dependency/version requirements to 0.4.0 to align release metadata.

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • lucifer1004/VeloQ#5: Introduces baseline SVG visualization pipeline that this PR extends with density-bin rendering and provenance metadata.
  • lucifer1004/VeloQ#6: Touches the same viz_timeline query wiring and fingerprint/types changes that this PR continues to evolve.

"🐰
I nudged pixels into tidy bins,
kept top kernels where light begins,
compacted crowded timeline skins,
and left the evidence — not its sins."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.90% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Refine NSys timeline visualization' directly describes the main feature added: density rendering and improved timeline visualization for NSys profiles, covering density binning, metadata reporting, and related documentation updates.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 vis-refine

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 and usage tips.

@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: 1

🤖 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 `@crates/veloq-vis/src/render/text.rs`:
- Around line 67-83: fit_interval_label currently ignores policy.min_label_px
and doesn't enforce policy.max_chars (aside from the zero-disable check); update
fit_interval_label to first treat policy.max_chars == 0 as before, then treat
the minimum visible width as the max of INTERVAL_LABEL_MIN_WIDTH_PX and
policy.min_label_px when comparing against raw_width, compute available_width as
before, then apply policy.max_chars by truncating label to at most
policy.max_chars characters (preserving any needed ellipsis logic if desired)
before measuring width with estimate_text_width(INTERVAL_LABEL_FONT_PX); set the
returned "truncated" flag if either the label was truncated to max_chars or its
measured width exceeds available_width, and return the possibly-truncated String
and the flag.
🪄 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

Run ID: 05b38517-4943-43c9-9994-e4e29f5c1869

📥 Commits

Reviewing files that changed from the base of the PR and between b3541a0 and bfb7df2.

⛔ Files ignored due to path filters (6)
  • Cargo.lock is excluded by !**/*.lock
  • docs/assets/social-card.png is excluded by !**/*.png
  • docs/assets/social-card.svg is excluded by !**/*.svg
  • examples/reports/nsys-timeline-vis/figures/hybrid-decode-overlap.svg is excluded by !**/*.svg
  • examples/reports/nsys-timeline-vis/figures/hybrid-prefill-pipeline.svg is excluded by !**/*.svg
  • examples/reports/nsys-timeline-vis/figures/tp-decode-contrast.svg is excluded by !**/*.svg
📒 Files selected for processing (34)
  • .agents/skills/nsys-profile-analysis/SKILL.md
  • .agents/skills/nsys-profile-analysis/references/cookbook.md
  • .agents/skills/nsys-profile-analysis/references/limitations.md
  • .claude-plugin/marketplace.json
  • .claude-plugin/plugin.json
  • .codex-plugin/plugin.json
  • AGENTS.md
  • CHANGELOG.md
  • Cargo.toml
  • README.md
  • crates/nsys/veloq-nsys-query/src/viz_timeline/fingerprint.rs
  • crates/nsys/veloq-nsys-query/src/viz_timeline/mod.rs
  • crates/nsys/veloq-nsys-query/src/viz_timeline/types.rs
  • crates/nsys/veloq-nsys-query/tests/wire_format_smoke.rs
  • crates/nsys/veloq-nsys/src/cli.rs
  • crates/nsys/veloq-nsys/src/commands/mod.rs
  • crates/nsys/veloq-nsys/src/error.rs
  • crates/nsys/veloq-nsys/src/help.rs
  • crates/nsys/veloq-nsys/src/views/basic.rs
  • crates/veloq-vis/src/model.rs
  • crates/veloq-vis/src/render/mod.rs
  • crates/veloq-vis/src/render/painter.rs
  • crates/veloq-vis/src/render/style.rs
  • crates/veloq-vis/src/render/tests.rs
  • crates/veloq-vis/src/render/text.rs
  • docs/index.html
  • docs/rfc/RFC-0009.md
  • examples/README.md
  • examples/reports/nsys-timeline-vis/README.md
  • examples/reports/nsys-timeline-vis/evidence/summary.json
  • examples/reports/nsys-timeline-vis/report.md
  • gov/rfc/RFC-0009/clauses/C-COMMAND-SURFACE.toml
  • gov/work/2026-06-10-add-timeline-density-rendering.toml
  • typos.toml

Comment on lines 67 to 83
pub(super) fn fit_interval_label(
label: &str,
raw_width: f64,
policy: &VizLabelPolicy,
) -> Option<(String, bool)> {
if raw_width < policy.min_label_px || policy.max_chars == 0 {
if policy.max_chars == 0 {
return None;
}
// 8px horizontal padding, ~6.4px average character width.
let width_chars = ((raw_width - 8.0) / 6.4).floor();
if width_chars < 4.0 {
// Text is clipped to the interval bar, so the string can be longer than
// the visible span. This threshold only prevents noisy one-letter labels.
if raw_width < INTERVAL_LABEL_MIN_WIDTH_PX {
return None;
}
let max_chars = policy.max_chars.min(width_chars as usize);
Some(truncate_label(label, max_chars))
let available_width = (raw_width - INTERVAL_LABEL_PADDING_X).max(0.0);
let truncated = estimate_text_width(label, INTERVAL_LABEL_FONT_PX) > available_width;
Some((label.to_string(), truncated))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Honor min_label_px and max_chars in label fitting

Line 72-Line 83 currently ignores policy.min_label_px and does not apply policy.max_chars (except the zero-disable case), which makes --min-label-px/--max-label-chars effectively non-functional at the render boundary.

Suggested fix
 pub(super) fn fit_interval_label(
     label: &str,
     raw_width: f64,
     policy: &VizLabelPolicy,
 ) -> Option<(String, bool)> {
     if policy.max_chars == 0 {
         return None;
     }
-    // Text is clipped to the interval bar, so the string can be longer than
-    // the visible span. This threshold only prevents noisy one-letter labels.
-    if raw_width < INTERVAL_LABEL_MIN_WIDTH_PX {
+    let min_width = policy.min_label_px.max(INTERVAL_LABEL_MIN_WIDTH_PX);
+    if raw_width < min_width {
         return None;
     }
-    let available_width = (raw_width - INTERVAL_LABEL_PADDING_X).max(0.0);
-    let truncated = estimate_text_width(label, INTERVAL_LABEL_FONT_PX) > available_width;
-    Some((label.to_string(), truncated))
+    let (fitted, truncated_by_chars) = truncate_label(label, policy.max_chars);
+    let available_width = (raw_width - INTERVAL_LABEL_PADDING_X).max(0.0);
+    let clipped_by_width = estimate_text_width(&fitted, INTERVAL_LABEL_FONT_PX) > available_width;
+    Some((fitted, truncated_by_chars || clipped_by_width))
 }
🤖 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 `@crates/veloq-vis/src/render/text.rs` around lines 67 - 83, fit_interval_label
currently ignores policy.min_label_px and doesn't enforce policy.max_chars
(aside from the zero-disable check); update fit_interval_label to first treat
policy.max_chars == 0 as before, then treat the minimum visible width as the max
of INTERVAL_LABEL_MIN_WIDTH_PX and policy.min_label_px when comparing against
raw_width, compute available_width as before, then apply policy.max_chars by
truncating label to at most policy.max_chars characters (preserving any needed
ellipsis logic if desired) before measuring width with
estimate_text_width(INTERVAL_LABEL_FONT_PX); set the returned "truncated" flag
if either the label was truncated to max_chars or its measured width exceeds
available_width, and return the possibly-truncated String and the flag.

@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.

♻️ Duplicate comments (1)
crates/veloq-vis/src/render/text.rs (1)

67-81: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Policy max_chars still not enforced (partial past-review fix).

Line 74 now correctly honors policy.min_label_px, but the function still does not enforce policy.max_chars. Users who set max_chars in the label policy expect character-count truncation before width-based clipping. Apply truncate_label(label, policy.max_chars) before the width estimation at line 79, and combine both truncation signals in the returned boolean.

Suggested fix
 pub(super) fn fit_interval_label(
     label: &str,
     raw_width: f64,
     policy: &VizLabelPolicy,
 ) -> Option<(String, bool)> {
+    if policy.max_chars == 0 {
+        return None;
+    }
     // Text is clipped to the interval bar, so the string can be longer than
     // the visible span. This threshold only prevents noisy one-letter labels.
     let min_visible_width = INTERVAL_LABEL_MIN_WIDTH_PX.max(policy.min_label_px);
     if raw_width < min_visible_width {
         return None;
     }
+    let (fitted, truncated_by_chars) = truncate_label(label, policy.max_chars);
     let available_width = (raw_width - INTERVAL_LABEL_PADDING_X).max(0.0);
-    let truncated = estimate_text_width(label, INTERVAL_LABEL_FONT_PX) > available_width;
-    Some((label.to_string(), truncated))
+    let clipped_by_width = estimate_text_width(&fitted, INTERVAL_LABEL_FONT_PX) > available_width;
+    Some((fitted, truncated_by_chars || clipped_by_width))
 }
🤖 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 `@crates/veloq-vis/src/render/text.rs` around lines 67 - 81, The function
fit_interval_label currently ignores policy.max_chars; before estimating width
in fit_interval_label, apply truncate_label(label, policy.max_chars) to get a
pre-truncated string, then use estimate_text_width on that truncated string
(with INTERVAL_LABEL_FONT_PX) and compute the width-based truncated flag as
before; finally return the truncated string and a combined boolean (true if
either truncate_label shortened the input or the width check set truncated) so
both character-count and pixel-based truncation are reported.
🤖 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.

Duplicate comments:
In `@crates/veloq-vis/src/render/text.rs`:
- Around line 67-81: The function fit_interval_label currently ignores
policy.max_chars; before estimating width in fit_interval_label, apply
truncate_label(label, policy.max_chars) to get a pre-truncated string, then use
estimate_text_width on that truncated string (with INTERVAL_LABEL_FONT_PX) and
compute the width-based truncated flag as before; finally return the truncated
string and a combined boolean (true if either truncate_label shortened the input
or the width check set truncated) so both character-count and pixel-based
truncation are reported.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1023e603-cca3-4a40-973d-0a72bf647086

📥 Commits

Reviewing files that changed from the base of the PR and between bfb7df2 and 6739f4a.

⛔ Files ignored due to path filters (6)
  • Cargo.lock is excluded by !**/*.lock
  • docs/assets/social-card.png is excluded by !**/*.png
  • docs/assets/social-card.svg is excluded by !**/*.svg
  • examples/reports/nsys-timeline-vis/figures/hybrid-decode-overlap.svg is excluded by !**/*.svg
  • examples/reports/nsys-timeline-vis/figures/hybrid-prefill-pipeline.svg is excluded by !**/*.svg
  • examples/reports/nsys-timeline-vis/figures/tp-decode-contrast.svg is excluded by !**/*.svg
📒 Files selected for processing (50)
  • .agents/skills/nsys-profile-analysis/SKILL.md
  • .agents/skills/nsys-profile-analysis/references/cookbook.md
  • .agents/skills/nsys-profile-analysis/references/limitations.md
  • .claude-plugin/marketplace.json
  • .claude-plugin/plugin.json
  • .codex-plugin/plugin.json
  • AGENTS.md
  • CHANGELOG.md
  • Cargo.toml
  • README.md
  • crates/nsys/veloq-nsys-query/src/viz_timeline/fingerprint.rs
  • crates/nsys/veloq-nsys-query/src/viz_timeline/mod.rs
  • crates/nsys/veloq-nsys-query/src/viz_timeline/types.rs
  • crates/nsys/veloq-nsys-query/tests/wire_format_smoke.rs
  • crates/nsys/veloq-nsys/src/cli.rs
  • crates/nsys/veloq-nsys/src/commands/mod.rs
  • crates/nsys/veloq-nsys/src/error.rs
  • crates/nsys/veloq-nsys/src/help.rs
  • crates/nsys/veloq-nsys/src/source.rs
  • crates/nsys/veloq-nsys/src/views/basic.rs
  • crates/veloq-vis/src/model.rs
  • crates/veloq-vis/src/render/mod.rs
  • crates/veloq-vis/src/render/painter.rs
  • crates/veloq-vis/src/render/style.rs
  • crates/veloq-vis/src/render/tests.rs
  • crates/veloq-vis/src/render/text.rs
  • crates/veloq/tests/cli_smoke/root.rs
  • docs/index.html
  • docs/rfc/RFC-0001.md
  • docs/rfc/RFC-0002.md
  • docs/rfc/RFC-0003.md
  • docs/rfc/RFC-0004.md
  • docs/rfc/RFC-0005.md
  • docs/rfc/RFC-0006.md
  • docs/rfc/RFC-0007.md
  • docs/rfc/RFC-0008.md
  • docs/rfc/RFC-0009.md
  • examples/README.md
  • examples/reports/nsys-timeline-vis/README.md
  • examples/reports/nsys-timeline-vis/evidence/summary.json
  • examples/reports/nsys-timeline-vis/report.md
  • gov/releases.toml
  • gov/rfc/RFC-0006/clauses/C-ARTIFACTS.toml
  • gov/rfc/RFC-0006/clauses/C-COMPATIBILITY.toml
  • gov/rfc/RFC-0006/clauses/C-SOURCE-IDENTITY.toml
  • gov/rfc/RFC-0006/rfc.toml
  • gov/rfc/RFC-0009/clauses/C-COMMAND-SURFACE.toml
  • gov/rfc/RFC-0009/clauses/C-COMPATIBILITY.toml
  • gov/work/2026-06-10-add-timeline-density-rendering.toml
  • typos.toml
✅ Files skipped from review due to trivial changes (18)
  • typos.toml
  • gov/rfc/RFC-0009/clauses/C-COMPATIBILITY.toml
  • gov/rfc/RFC-0006/rfc.toml
  • .claude-plugin/plugin.json
  • .claude-plugin/marketplace.json
  • gov/releases.toml
  • docs/rfc/RFC-0003.md
  • docs/rfc/RFC-0007.md
  • docs/rfc/RFC-0001.md
  • examples/README.md
  • docs/rfc/RFC-0002.md
  • examples/reports/nsys-timeline-vis/evidence/summary.json
  • docs/rfc/RFC-0005.md
  • docs/rfc/RFC-0004.md
  • CHANGELOG.md
  • docs/rfc/RFC-0008.md
  • examples/reports/nsys-timeline-vis/README.md
  • .agents/skills/nsys-profile-analysis/references/cookbook.md
🚧 Files skipped from review as they are similar to previous changes (11)
  • gov/rfc/RFC-0009/clauses/C-COMMAND-SURFACE.toml
  • docs/index.html
  • crates/nsys/veloq-nsys-query/tests/wire_format_smoke.rs
  • gov/work/2026-06-10-add-timeline-density-rendering.toml
  • crates/nsys/veloq-nsys/src/cli.rs
  • crates/nsys/veloq-nsys/src/error.rs
  • crates/nsys/veloq-nsys-query/src/viz_timeline/fingerprint.rs
  • crates/nsys/veloq-nsys/src/views/basic.rs
  • crates/veloq-vis/src/render/painter.rs
  • crates/nsys/veloq-nsys/src/commands/mod.rs
  • crates/veloq-vis/src/render/mod.rs

@lucifer1004
lucifer1004 merged commit 8632bf8 into main Jun 11, 2026
8 of 9 checks passed
@lucifer1004
lucifer1004 deleted the vis-refine branch June 12, 2026 01:29
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