github robintra/perf-sentinel v0.9.10

latest release: chart-v0.9.10
4 hours ago

What's new in v0.9.10

v0.9.10 is a minor release confined to the self-contained HTML report. The dashboard is reskinned to the hi-fi design handoff, its Overview KPI cards become semantic and clickable, and the panels gain sortable tables, a density toggle, search-match highlighting, and an undo on acknowledgment. Nothing outside the HTML sink moves: the pipeline, the detectors, ingest, the daemon routes, the JSON and SARIF output, and the Content-Security-Policy are untouched, and a report rendered from the same input embeds the same analysis payload, with only its version field advanced.

Report: the dashboard reskin

The embedded brand typefaces change from Geist to DM Sans for the interface and JetBrains Mono for code, both embedded as base64 woff2 latin subsets under OFL-1.1. The report gains two weights (eight faces against six) and still gets lighter: the embedded font stylesheet drops from 221 KB to 193 KB, so a rendered dashboard is about 28 KB smaller than the v0.9.9 equivalent. The fonts stay inline, so the report still renders offline with no network fetch and the strict CSP (default-src 'none', font-src data:) is unchanged.

The visual language follows the design handoff rather than accumulating overrides: the design tokens were merged into the original rules. Every gradient is replaced by a flat pastel fill, cards drop their borders and hold floating white rows on a grey surface, the sidebar navigation becomes pills, buttons become soft-green pills carrying CSS-mask icons, and links underline with a green hover highlight. Modals, toasts, scrollbars, and the daemon status dots now read their colors from the theme tokens instead of hardcoded hex, so the two themes stay consistent. Text sizes gain a readability floor, 12px for content and 11.5px for metadata, with 11px reserved for the spaced uppercase eyebrows.

Report: semantic KPI cards and real impact ordering

The Overview KPI row stops asserting colors it cannot justify. The Findings card is a solid semantic fill driven by the highest severity actually present, green when the report is clean, blue when only info findings exist, orange on warnings, red as soon as one critical appears. The card beside it previously read Critical in permanent red whatever the count, and now promotes the dominant severity: its label, its number, and its pastel tint follow the highest severity present, and its sub-line lists only the severities below it. Δ Baseline tints red on a net regression and green on a net improvement. The carbon amounts render neutral, since an amount is not a verdict, and Avoidable keeps its green accent only when there is something to avoid. Each card is a shortcut to its tab, operable by mouse and by keyboard, and the promoted-severity card opens Findings already filtered on that severity.

The Overview list titled "Top findings by impact" now earns its name. It previously showed the first five findings in the deterministic detector order, which sorts by type before severity, so the label was misleading. The list is ranked by severity, then by avoidable I/O operations, then by interaction-intensity score. The ranking is applied to a copy at render time in the report only. detect::sort_findings keeps its deterministic key, so the JSON, SARIF, CSV, and console outputs and any baseline diff are ordered exactly as before.

Report: table sorting, density, and search

Every table with an identity gains sortable headers. A click cycles descending, ascending, and back to the report's own order, with numeric columns starting descending and text columns ascending. Shift-click adds a second column as a tie-breaker, and the arrows number themselves when several keys are active. Severity pills sort by severity rank rather than alphabetically, numbers are parsed with their unit so a CO2 column mixing grams, milligrams, and micrograms compares magnitudes correctly, and columns that only look numeric, such as an ISO-8601 expiry or a hex signature, stay textual. A highlighted pg_stat row stays pinned while a sort is active. The sort is announced through aria-sort, reachable by keyboard, persisted per table in the browser, and carried by Copy link through a tsort hash key scoped to the shared tab. The key is unknown to the hash router, which ignores it, so deep links behave exactly as before.

The report opens in comfort density, and a topbar toggle switches to a compact layout that fits more rows per screen. The choice persists in the browser and is applied before the first paint, so there is no flash of the other mode. Search gains match highlighting inside the filtered rows, and the topbar hint shows the platform's real shortcut, Command-K on macOS and Ctrl-K elsewhere, for a binding that already accepted both.

In live mode, acknowledging a finding now offers an eight-second undo in the toast, which deletes the acknowledgment without the confirmation dialog. The Revoke button on a row keeps its confirmation, and the undo path handles a rotated API key the same way Revoke does, by reopening the authentication modal and retrying.

Report: fixes

The Acknowledgments tab badge showed a stale 0 beside the live count, rendering as "0 3", because the badge sync targeted .ps-badge while the tab renders .ps-nav-badge, so it never found the existing badge and appended a second one.

The trace tree was hard to read in three ways. Spans at depth zero had no base indent and sat flush against the edge. The highlight was always red regardless of the finding, and now follows its severity. Whole-trace findings (chatty_service, pool_saturation, serialized_calls) match no span template, so every row was dimmed with nothing highlighted, which made the tree unreadable. Dimming now only applies when at least one span actually matches.

In live mode, the container around the Ack button swallowed clicks across the full width of the row, so the band beside the button was a dead zone that would not open the finding. Only the button itself now intercepts.

Operator-visible behavior change

The dashboard looks different, in typeface, color, and density. Reports rendered by this binary carry the new UI preferences in the browser's local storage, under perf-sentinel:density and perf-sentinel:tablesort, alongside the existing session-scoped API key, which stays in session storage and is never persisted. The Δ baseline KPI label is now Δ Baseline, and the Overview lists the top five findings in a different order than v0.9.9 for the same input, since it is now ranked by impact. No CLI flag, configuration key, output schema, daemon HTTP route, metric, or wire shape is added or changed. The JSON payload embedded in the report keeps its schema and its analysis data, with only version and binary_version advancing to 0.9.10.

Why this is a minor and not a patch

The release adds interactive capabilities the report did not have, table sorting, a density mode, search highlighting, and an acknowledgment undo, which is more than restoring intended behavior. It also changes what an operator sees on every screen, including the ordering of the Overview top five and the promoted severity card, so the output is not identical on identical input. Nothing an integration depends on moves: the payload schema, the CLI surface, the configuration, the daemon routes, the metrics, and the CSP are unchanged, and the minimum supported Rust version stays at 1.96.1.

Validation

This release changes no schema, configuration, API, metric, ingest path, or detector, so it carries no simulation-lab gate line, following the precedent set by v0.8.4. The lab exercises the daemon and the batch pipeline, neither of which this release touches.

The HTML surface is validated where it lives. The browser suite passes 13 of 13 against a report rendered by the release binary, including two new specs covering the comfort default with its persistence across a reload, and the real row ordering of a sort rather than only its aria-sort attribute. The demo suite passes 22 of 22 and regenerates every documentation still from the final template. The Rust suite asserts the template contract that the reskin had to keep: the strict CSP with font-src data:, the eight embedded woff2 faces, the ban on innerHTML and eval that forces every new script onto textContent and createElement, the eight ARIA tab panels, the escaped </script> in the embedded payload, and that no local-storage write may carry the daemon API key. CI covers the build, the full test suite (cargo test --workspace, 2598 tests), clippy at -D warnings, cargo fmt, and both the default and --no-default-features builds.

Verifying this release

# Binary integrity via SLSA Build L3 attestation
gh attestation verify perf-sentinel-linux-amd64 \
  --owner robintra --repo perf-sentinel

# A periodic disclosure produced by this binary
perf-sentinel verify-hash --report perf-sentinel-report.json \
  --expected-identity "https://github.com/robintra/perf-sentinel/.github/workflows/release.yml@refs/tags/v0.9.10" \
  --expected-issuer "https://token.actions.githubusercontent.com"

gh CLI 2.49 or newer required for gh attestation verify.

Full Changelog: v0.9.9...v0.9.10

Don't miss a new perf-sentinel release

NewReleases is sending notifications on new releases.