What's new in v0.6.0
v0.6.0 is the first SemVer-incompatible release of the 0.x line. Three things break together. The public Config API splits into sectioned sub-structs (thresholds, detection, green, daemon), the eight legacy top-level keys deprecated since 0.5.26 are removed so a 0.5.x .perf-sentinel.toml that still uses any of them now fails at load with an explicit migration error, and the OTLP ingest path no longer reaches into report::metrics::MetricsState directly. A new MetricsSink trait owned by ingest closes a long-standing layering leak. The audit pass also lands its first wave of user-facing alignments: the CLI says "Found N finding(s)" instead of "issue(s)", fan-out becomes fanout to match the ExcessiveFanout enum and the excessive_fanout label, eight tautological template_carries_* HTML drift guards are dropped in favor of a typed API_KEY_HEADER lockstep check, and a Grafana Pyroscope row joins the README comparison table to position the two tools as complementary.
Breaking: Config sectioned API
Config splits into four sub-structs reachable as config.thresholds, config.detection, config.green, config.daemon. Library consumers that read config.green_default_region, config.tls_cert_path, config.n_plus_one_threshold etc. need to migrate to the nested form. The RawConfig to Config adapter still accepts the section + flat-keys mix on disk for keys that have a section equivalent, the eight legacy top-level keys listed below are the only ones that now hard-fail.
Breaking: 8 legacy top-level config keys removed
The keys deprecated with a WARN since 0.5.26 are gone. Loading a .perf-sentinel.toml that still uses any of them returns a ConfigError::Validation whose message names both the removed key and its replacement, so the error stream tells you exactly what to edit.
| Removed (top-level) | Use instead | Section |
|---|---|---|
n_plus_one_threshold
| n_plus_one_min_occurrences
| [detection]
|
window_duration_ms
| window_duration_ms
| [detection]
|
n_plus_one_sql_critical_max
| n_plus_one_sql_critical_max
| [thresholds]
|
n_plus_one_http_warning_max
| n_plus_one_http_warning_max
| [thresholds]
|
io_waste_ratio_max
| io_waste_ratio_max
| [thresholds]
|
listen_port
| listen_port_http
| [daemon]
|
max_events_per_trace
| max_events_per_trace
| [daemon]
|
max_payload_size
| max_payload_size
| [daemon]
|
Breaking: MetricsSink trait closes the ingest -> report leak
Before 0.6.0 the OTLP ingest path imported report::metrics::MetricsState directly to record per-protocol rejection counters. That meant ingest could only be enabled when report::metrics was compiled in, and MetricsState was part of every public OTLP handler signature. v0.6.0 introduces pub trait MetricsSink: Send + Sync in crates/sentinel-core/src/ingest/otlp.rs, with one method record_otlp_reject(&self, reason: OtlpRejectReason). MetricsState implements the trait in crates/sentinel-core/src/report/metrics.rs. The OTLP HTTP and gRPC handlers and the daemon listener layer now accept Option<Arc<dyn MetricsSink>> instead of Option<Arc<MetricsState>>. The dependency direction is inverted: ingest no longer depends on report, report provides an implementation of the trait ingest defines. CLI consumers see no behavior change, the /metrics exemplars are byte-identical for already-clean inputs.
Lexicon alignment from the audit
The user-facing vocabulary was drifting across CLI output, docs, the data model, and the SARIF / JSON / API surfaces. v0.6.0 lands the first wave of alignments.
- CLI:
Found N finding(s)instead ofFound N issue(s). The CLI integration test asserts the new wording. - Spelling: a single
Acknowledgement(UK) drift indocs/SUPPLY-CHAIN.mdwas aligned to the 386Acknowledgment(US) occurrences elsewhere. - Detector naming:
fan-outbecomesfanoutacross docs (EN + FR mirrors), aligning prose with theExcessiveFanoutenum and the snake_caseexcessive_fanoutfinding label. - Glossary:
docs/ARCHITECTURE.mdand the FR mirror gain a Glossary section that pins downeventvsfindingvspatternvsdetection, plus the four operating modes (batch,CI,daemon,watch) and theConfidenceaxis. - Edge cases:
docs/ACK-WORKFLOW.mdadds a "Service renames invalidate acks" section covering signature breakage onservice.namechange,http.routerefactors, and SQL/HTTP template changes.docs/LIMITATIONS.mdadds a "Long-running traces and TTL eviction" section explaining why sparse-burst traces undercount in streaming mode and how to mitigate. - Comparison table:
README.mdandREADME-FR.mdadd a Grafana Pyroscope column to the capability table and a "Not a continuous profiler" entry. The framing is complementary, not competitive: Pyroscope tells you where compute time goes, perf-sentinel tells you which I/O patterns drive that time.
Tests: drop 8 tautological template_carries_*
Eight template_carries_* HTML drift guards in crates/sentinel-core/src/report/html.rs were tautologies, each asserting that a literal HTML id, class, or function name appeared in the static template with no semantic check (template_carries_daemon_status_badge_id, template_carries_refresh_button_id, template_carries_acknowledgments_panel_id, template_carries_include_acked_toggle_id, template_carries_auth_modal, template_carries_ack_modal, template_carries_session_api_key_storage_constant, template_carries_boot_live_mode_function). v0.6.0 removes them and consolidates the X-API-Key header value into a single source of truth, pub const API_KEY_HEADER: &str = "X-API-Key"; in crates/sentinel-core/src/http_client.rs. The new template_propagates_api_key_header_constant test asserts the live-mode JS propagates the same constant, replacing eight literal-string drift guards with one typed lockstep check. fetch_with_body and the daemon check_ack_auth both consume the constant through crate::http_client::API_KEY_HEADER. Net: -8 tests, +1 test, behavior unchanged.
Kept as-is for real semantic value:
template_carries_scoring_config_bandeau_and_helpers(drift guard for the 0.5.12 scoring bandeau JS)template_carries_estimated_column_and_helper(drift guard for the 0.5.10 estimated-energy JS)template_carries_csp_placeholder(CSP placeholder ordering invariant required by thereplacensubstitution)live_mode_acks_cap_matches_daemon_constantnow parsesDAEMON_ACKS_CAPout of the template and asserts equality againstdaemon::query_api::MAX_ACKS_RESPONSE. The 0.5.x version asserted a literal1000on one side only.
Added
MetricsSinktrait incrates/sentinel-core/src/ingest/otlp.rs, decoupling the OTLP ingest fromMetricsState.MetricsStateis the only built-in implementation today, the OTLP handlers now accept anyArc<dyn MetricsSink>.pub const API_KEY_HEADER: &str = "X-API-Key";incrates/sentinel-core/src/http_client.rs. The daemoncheck_ack_authand the outboundfetch_with_bodyboth consume it through this single constant.- Glossary section in
docs/ARCHITECTURE.md(and FR mirror) coveringevent/finding/pattern/detectionplus the four operating modes (batch,CI,daemon,watch) and theConfidenceaxis. docs/ACK-WORKFLOW.md"Service renames invalidate acks" section (and FR mirror), documenting howservice.namerenames,http.routerefactors, and SQL/HTTP template churn invalidate existing acknowledgments.docs/LIMITATIONS.md"Long-running traces and TTL eviction" section (and FR mirror), explaining sparse-burst undercounting in streaming mode and the mitigation knobs.- Grafana Pyroscope column in the README comparison table and a "Not a continuous profiler" entry positioning the two tools as complementary.
Changed
- CLI:
Found N issue(s)becomesFound N finding(s). The CLI integration test asserts the new wording so it stays in lockstep with the data model and the JSON / SARIF / API surfaces. fan-outbecomesfanoutacross EN and FR docs, aligning prose with theExcessiveFanoutenum and theexcessive_fanoutlabel.docs/SUPPLY-CHAIN.mdAcknowledgementaligned toAcknowledgmentto match the 386 US-spelling occurrences elsewhere.live_mode_acks_cap_matches_daemon_constantparsesDAEMON_ACKS_CAPfrom the template and asserts equality againstdaemon::query_api::MAX_ACKS_RESPONSEinstead of a one-sided literal1000.- OTLP HTTP and gRPC handlers and the daemon listener layer accept
Option<Arc<dyn MetricsSink>>instead ofOption<Arc<MetricsState>>.
Removed
- 8 legacy top-level config keys (
n_plus_one_threshold,window_duration_ms,n_plus_one_sql_critical_max,n_plus_one_http_warning_max,io_waste_ratio_max,listen_port,max_events_per_trace,max_payload_size). Loading hard-fails withConfigError::Validationinstead of falling back to the section default with aWARN. - 8 tautological
template_carries_*tests incrates/sentinel-core/src/report/html.rs. tracing-test = "0.2.6"dev-dependency onperf-sentinel-core. It was only used by the legacy-flat deprecation tests, which the legacy keys removal made redundant.
Internal
Configis four sub-structs (thresholds,detection,green,daemon). TheRawConfigtoConfigadapter still accepts the section + remaining flat-keys mix on disk. Per-sectionvalidate_*functions centralize range and consistency checks.- CI fix:
live_mode_acks_cap_matches_daemon_constant, thefresh_metrics_sinktest helper, and theMetricsStatetest import iningest/otlp.rsare now gated by#[cfg(feature = "daemon")].cargo check -p perf-sentinel-core --no-default-features --all-targetsis warning-clean.
Notes
- Migration steps: edit
.perf-sentinel.tomlto move the eight removed keys into their respective sections before upgrading. The error message names the replacement, so a single pass on the load error tells you exactly what to edit. Library consumers also need to swapconfig.green_*,config.tls_*,config.n_plus_one_*for theconfig.<section>.*form. - Wire format unchanged: JSON, SARIF, HTML, TOML ack store, and Prometheus
/metricsoutputs are byte-identical to v0.5.28 for already-clean inputs. No data on disk needs migration, only the configuration file and library imports. - Helm chart:
charts/perf-sentinel/Chart.yamlis still pinned atversion: 0.2.31/appVersion: 0.5.28. A chart bump pinning0.6.0will follow.
Install
Pre-built static binaries are attached to this release for linux-amd64, linux-arm64, macos-arm64, and windows-amd64. Verify the SHA256 from SHA256SUMS.txt before extracting. Crate consumers can cargo install perf-sentinel --version 0.6.0 once the workflow finishes propagating to crates.io.
Full Changelog: v0.5.28...v0.6.0