v0.7.8
What's new in v0.7.8
v0.7.8 closes the suggested_fix coverage gaps surfaced by the v0.7.7 simulation lab and extends N+1 detection to HTTP. Three additive detection changes: vendor-specific OTel scope recognition (.NET EF Core, Quarkus), a last-resort service-name fallback for framework detection, and HTTP sanitizer-aware classification. Lab validation against 12 multistack services reached 118/120 findings (98.3%), with suggested_fix coverage at 11/12, up from 8/12 in v0.7.7. No config breaking change, no CLI surface change, no daemon wire protocol change, no report JSON schema change.
Vendor-specific OTel scope recognition
Framework detection previously only matched instrumentation scopes following the io.opentelemetry.* / opentelemetry.instrumentation.* / @opentelemetry/instrumentation-* conventions. Stacks whose OTel scope sits outside those conventions got no suggested_fix. A new VENDOR_SCOPE_RULES table, checked before the standard SCOPE_RULES, resolves a framework for these:
| Scope | Framework tag | Stack |
|---|---|---|
OpenTelemetry.Instrumentation.EntityFrameworkCore |
csharp_ef_core |
.NET EF Core (OTel wrapper) |
Microsoft.EntityFrameworkCore |
csharp_ef_core |
.NET EF Core (raw NuGet scope) |
io.quarkus.hibernate.reactive, io.quarkus.panache.reactive, io.quarkus.reactive |
java_quarkus_reactive |
Quarkus reactive |
io.quarkus |
java_quarkus |
Quarkus (catch-all) |
Matching is segment-boundary aware (vendor_prefix_matches): the prefix must end at a . boundary or consume the whole scope, so io.quarkusbridge.acme does not match io.quarkus. Reactive sub-packages are checked before the catch-all so a reactive Quarkus app does not fall through to the generic tag. This resolves dotnet-svc (csharp_ef_core) and mutiny-svc (java_quarkus) in the lab, both of which previously had no suggested_fix.
Service-name fallback for framework detection
When the instrumentation scope chain, code_location namespace, and filepath are all absent, detect_framework now falls back to a last-resort scan of the finding's service name (SERVICE_NAME_RULES). A distinctive framework substring in the service name (e.g. helidon in helidon-se-svc) infers the framework. Entries are limited to names distinctive enough to avoid false positives, generic terms like diesel, gorm, prisma, and quarkus are intentionally excluded from this table (they remain handled by the higher-confidence scope and namespace paths). This resolves helidon-se-svc (java_helidon_se), which emits no usable scope or code location.
HTTP N+1 sanitizer-aware classification
The sanitizer-aware heuristic, SQL-only since v0.5.7, now also classifies HTTP outbound groups. classify_group dispatches on event type: SQL keeps its existing looks_sanitized path, HTTP routes to a new classify_http_group_indexed. HTTP groups that fail the direct distinct-params rule are reclassified from redundant_http to n_plus_one_http when:
Auto/Always: timing variance suggests N+1 (CV>= 0.5).Strict: a primary signal (HTTP placeholder in the template, high occurrence, or sequential siblings) corroborated by timing variance.
Unlike the SQL path, high occurrence alone is not sufficient corroboration for HTTP, because HTTP has no looks_sanitized gate to first filter out non-sanitized groups. A high-occurrence HTTP group with uniform timing (health-check poll, retry storm) stays redundant_http.
Known limitation: .NET query-string redaction
N+1 HTTP detection requires the varying request parameter to be visible in the span. OpenTelemetry .NET System.Net.Http redacts the query string to ?* by default, so a query-parameter N+1 loop (GET /api/mock?seq=1, ?seq=2, ...) reaches perf-sentinel as byte-identical URLs and is classified as redundant_http, not n_plus_one_http. The distinguishing parameter is destroyed inside the .NET process, so no trace consumer (Jaeger, Tempo, or any OTLP backend) can recover it. Operators who rely on query-parameter HTTP N+1 detection set OTEL_DOTNET_EXPERIMENTAL_HTTPCLIENT_DISABLE_URL_QUERY_REDACTION=true, or model the varying identifier as a path segment (/api/resource/{id}). This is the HTTP analogue of the documented SQL bind-parameter visibility limit. Documented in docs/LIMITATIONS.md, the detection design notes, and the .NET instrumentation guide (EN + FR).
Security
safeHttpsHref in the self-contained HTML dashboard now rejects C0 controls, DEL, and C1 controls (\x00-\x1f\x7f-\x9f) in addition to the existing https:// scheme lock, completing the control-character guard. The reference_url values it renders are all compile-time-static and HTTPS-only on an allowlisted domain set, so this is defense-in-depth against a future code path feeding it an externally-sourced URL.
Documentation
docs/LIMITATIONS.mdand its FR mirror: new "HTTP query-string redaction and N+1 visibility" section (TOC entry + body), placed next to the SQL ORM bind-parameter analogue.docs/design/04-DETECTION.mdand its FR mirror: new "HTTP extension (0.7.8+)" subsection under sanitizer-aware classification documenting the HTTP signal set, plus a "Known limit: query-string redaction" note.docs/INSTRUMENTATION.mdand its FR mirror: a note in the .NET section, next to the existing EF Core bind-parameter note, pointing operators at the query-redaction env var.
Tests
The diff adds tests across the new code paths:
suggestions.rs: vendor scope recognition (.NET EF Core, Quarkus reactive vs catch-all,vendor_prefix_matchesdot-boundary, cross-table precedence) and service-name fallback (helidon-se, helidon-mp precedence, scope-wins-over-service-name, generic-name rejection).sanitizer_aware.rs: HTTP placeholder recognition andclassify_http_group_indexedacross all four modes (Auto variance, Never inconclusive, Strict placeholder+variance, Strict high-occurrence-no-variance stays inconclusive, Strict sequential+variance).n_plus_one.rs: HTTP heuristic end-to-end throughdetect_n_plus_one(Auto reclassify on variance, low-variance no finding, Never no finding, Always unconditional, Strict placeholder+variance, Strict no-signal no finding).
Why this is a patch and not a minor
Every change is additive. The vendor scope and service-name tables only make existing Framework enum variants reachable for previously-undetectable stacks, no new framework_tag value is introduced. HTTP N+1 reclassification changes some findings from redundant_http to n_plus_one_http but adds no config key and changes no JSON shape, both finding types contribute identically to GreenOps scoring. The CLI subcommand surface, daemon HTTP routes, OTLP wire protocol, report JSON schema, co2.model enum, and Prometheus metric names and label sets are byte-for-byte identical to v0.7.7.
Operator-visible behavior change
HTTP workloads under sanitizer_aware_classification = "strict" (or auto/always) may see some groups previously reported as redundant_http (Warning) now reported as n_plus_one_http (Critical at >= 10 occurrences) when they show timing variance. CI gates wired on critical-only count may flag groups that were previously only at warning level. Same recommendation as the v0.7.7 SQL change: re-baseline n_plus_one_http_critical_max on a representative trace sample, or treat the upgrade as the moment to address those patterns. GreenOps scoring is unchanged.
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.7.8" \
--expected-issuer "https://token.actions.githubusercontent.com"gh CLI 2.49 or newer required for gh attestation verify (unchanged from v0.7.2).
Full Changelog: v0.7.7...v0.7.8