What's new in v0.9.7
v0.9.7 is a bugfix release. Batch analyze --input rejected a legitimate OTLP JSON attribute whose value is an empty list, failing the entire file, and this release accepts it. Canonical protobuf JSON omits empty repeated fields, so an empty list serializes with no values key, and one such attribute anywhere in a dump made the whole file unanalyzable. The pipeline stages, the detection logic, the CLI surface, the daemon, and every wire format are unchanged.
Ingestion: empty-list OTLP JSON attributes
Canonical protobuf JSON drops empty repeated fields, so an OTLP list attribute with no elements serializes as {"arrayValue":{}}, and an empty key-value list as {"kvlistValue":{}}, with the inner values field omitted. analyze --input read the OTLP JSON straight into the generated ExportTraceServiceRequest type, whose derived deserialization marks values as required, so a single such attribute failed the whole file with missing field values and a non-zero exit, with nothing analyzed. Real producers emit this shape: the OpenTelemetry Astronomy Shop demo's recommendation service writes app.filtered_products.list as an empty list whenever its cache-failure flag is on.
The OTLP JSON reader now keeps the strict typed parse as the fast path, so a well-formed request still gets duplicate-key rejection, positioned parse errors, and streaming memory. It backfills the omitted empty list only for the specific document that trips missing field values, leaving the common case untouched. A malformed document, a truncated tail, or a genuine type mismatch still fails as it did before.
Operator-visible behavior change
An OTLP JSON file that analyze --input (and explain, diff, calibrate, report --input) previously rejected with missing field values now ingests and analyzes. Nothing else changes: a file that already parsed produces the same result, and no CLI flag, configuration key, output format, daemon route, or wire shape is added or changed.
Why this is a patch and not a minor
This is a single bug fix that widens ingest tolerance to a form the OTLP JSON mapping has always allowed. It adds no surface, no new CLI flag, configuration default, output format, daemon HTTP route, or wire shape, and it removes nothing. A file that already analyzed behaves identically on the same input. The minimum supported Rust version is unchanged.
Validation
The simulation-lab release gate passed for v0.9.7 (lab commit ad2c2d3, 2026-07-10). The astronomy-shop scenario replays a committed OTLP JSON slice whose degraded corpus carries six of these empty-list attributes from real community auto-instrumentation, so its recall check is the end-to-end regression test for this fix: the slice now analyzes to 231 traces with the expected n_plus_one_sql, redundant_sql, and redundant_http findings, and the false-positive budget on the clean slice holds. CI covers the build, the full test suite (cargo test --workspace), 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.7" \
--expected-issuer "https://token.actions.githubusercontent.com"gh CLI 2.49 or newer required for gh attestation verify.
Full Changelog: v0.9.6...v0.9.7