dev-report 0.9.6

Structured JSON reports for Rust verification tooling. Stable, versioned wire format with optional terminal, markdown, SARIF, and JUnit XML rendering. Foundation of the dev-* collection.
Documentation
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.9.6] - 2026-05-12

Markdown table hardening surfaced by the post-polish audit pass.

### Fixed

- `Diff::to_markdown()` (markdown feature) now backslash-escapes `|` and replaces line breaks with `<br>` inside check-name cells of the *Severity changes* and *Duration regressions* tables. A check named `a|b|c` previously corrupted the surrounding `|`-delimited table layout, shifting every later column. Newlines had the same effect. Both forms are now safe.

### Internal

- New helper `escape_table_cell()` in the markdown module — `|` becomes `\|`, `\n`/`\r` becomes `<br>`.
- New test `diff_table_escapes_pipes_in_check_names` exercises the regression with a check name containing two pipes.

[0.9.6]: https://github.com/jamesgober/dev-report/releases/tag/v0.9.6

## [0.9.5] - 2026-05-12

Bug-fix release surfaced by the post-polish audit pass.

### Fixed

- `Evidence::numeric()` now coerces `NaN`, `+Infinity`, and `-Infinity` to `0.0` at construction. Previously, a `Report` containing a non-finite numeric value would compile cleanly and accept the value, then panic at serialization time inside `to_json()`, `to_sarif()`, or any other JSON-emitting path (`serde_json::Number::from_f64` returns `None` for non-finite, and the surrounding code unwrapped that). Real-world measurements occasionally produce `NaN` (e.g. division-by-zero in custom percentile maths) — coercing at the constructor lets the report survive the round-trip.

### Internal

- New test `evidence_numeric_coerces_nan_and_inf_to_zero` pins the behavior for `NaN`, `+Inf`, and `-Inf`, and exercises a full `Report::to_json()` round-trip with a non-finite measurement.

[0.9.5]: https://github.com/jamesgober/dev-report/releases/tag/v0.9.5

## [0.9.4] - 2026-05-12

Documentation and SEO pass. No code changes.

### Changed

- README header standardized to match the collection-wide template: Rust logo image, MSRV badge positioned between CI and docs.rs, copyright block at the bottom.
- Subtitle now reads `STRUCTURED VERIFICATION REPORTS FOR RUST` (was `STRUCTURED REPORTS FOR AI-ASSISTED RUST DEVELOPMENT`). The crate targets every Rust crate maintainer, not only AI tooling.
- Tagline rewritten to lead with what the schema *does* (produce / consume / diff) rather than its internal role.
- `## The dev-* suite` section retitled to `The dev-* collection` and expanded from 6 to 14 sibling crates (the full current suite). Crate links now point at crates.io.
- `Cargo.toml` `description` rewritten: leads with the actual feature set (JSON, versioned, with SARIF/JUnit/markdown/terminal output) instead of the AI-assisted framing.
- `Cargo.toml` `keywords` retuned: `testing`, `reporting`, `sarif`, `junit`, `ci` (was `testing`, `verification`, `reporting`, `ci`, `ai-tools`).

### Added

- "Foundation of the `dev-*` verification collection" content block on the README, sized so the suite relationship is visible at a glance without crowding the intro.

[0.9.4]: https://github.com/jamesgober/dev-report/releases/tag/v0.9.4

## [0.9.3] - 2026-05-12

### Added

- JSON Schema document at `schema/report.schema.json` (Draft 2020-12) describing the wire format for `Report` and `MultiReport`. Field-level descriptions cover `Verdict`, `Severity`, `Evidence`, `EvidenceData`, `FileRef`, `CheckResult`, `Report`, and `MultiReport`. Lets TypeScript, Python, Go, and `jq` consumers validate or generate dev-report documents without touching Rust.
- `examples/schema_sample.rs` emits a JSON document exercising every schema variant. CI uses it as the validation sample.
- `scripts/validate_schema.py` validates JSON documents against `schema/report.schema.json` using the `jsonschema` Python package.
- New `sarif` feature: `Report::to_sarif()` and `MultiReport::to_sarif()` emit SARIF 2.1.0 documents. Only `Fail` and `Warn` checks are emitted; pass and skip are omitted (SARIF is a defect-report format). Severity maps to SARIF `level` (`Critical`/`Error` → `error`, `Warning` → `warning`, `Info` → `note`). `Evidence::FileRef` becomes a SARIF `physicalLocation` with optional line range. `MultiReport` emits one SARIF `run` per constituent producer. No new runtime dependencies; uses the existing `serde_json`.
- New `junit` feature: `Report::to_junit_xml()` and `MultiReport::to_junit_xml()` emit Jenkins/Surefire JUnit XML documents. Pass/Warn become self-closing `<testcase>`; Fail produces `<failure>` with a severity-derived `type` attribute; Skip produces `<skipped/>`. Durations propagate as `time` attributes in seconds. XML attribute and text values are escaped. No external dependencies.
- `examples/sarif_export.rs` and `examples/junit_export.rs` demonstrate each exporter.
- `scripts/validate_sarif.py` performs structural validation of the SARIF subset dev-report emits.
- `scripts/validate_junit.py` round-trips JUnit XML through `xml.etree.ElementTree` and cross-checks declared counts against child element counts.

### Changed

- CI: `actions/checkout` bumped to `v5` (was `v4`); removes Node 20 deprecation warnings.
- CI: new `wire-format` job generates samples for every output format (JSON, SARIF, JUnit) and validates each.
- REPS.md § 7 now binds the JSON wire format to `schema/report.schema.json` as the canonical contract.
- No schema changes. `schema_version` stays at `1`. SARIF and JUnit exporters do not alter the JSON wire format.

[0.9.3]: https://github.com/jamesgober/dev-report/releases/tag/v0.9.3

## [0.9.2] - 2026-05-10

### Added

- `Diff::summary()` returns a one-line human-readable summary, e.g. `"clean"` or `"2 newly failing, 1 added"`. Useful for log output and CI status lines without parsing the full structure.
- `Report::set_started_at(ts)` and `Report::set_finished_at(ts)` setters for replay/import scenarios where the real timestamps are known but `Utc::now()` would be wrong.
- `Report::verdict_counts() -> (pass, fail, warn, skip)` for one-shot count aggregation.
- `MultiReport::iter_reports()` iterator over constituent reports.
- `MultiReport::report_from(producer_name)` lookup by producer.
- `MultiReport::verdict_counts()` aggregate across all constituent reports.

### Changed

- No schema changes. `schema_version` stays at `1`.

[0.9.2]: https://github.com/jamesgober/dev-report/releases/tag/v0.9.2

## [0.9.1] - 2026-05-09

### Added

- `Report::passed()` / `failed()` / `warned()` / `skipped()` shortcut methods (and the same set on `MultiReport`).
- `Report::checks_with_severity(severity)` and `MultiReport::checks_with_severity(severity)` filter iterators.
- `Evidence::numeric_int(label, i64)` constructor for integer counters that may exceed `f64`'s 53-bit exact range.
- `Diff::to_terminal()` / `to_terminal_color()` / `to_markdown()` rendering methods (gated by the `terminal` and `markdown` features respectively).
- `MultiReport::to_terminal()` / `to_terminal_color()` / `to_markdown()` rendering methods.
- New free functions in the `terminal` and `markdown` modules: `diff_to_terminal`, `diff_to_terminal_color`, `multi_to_terminal`, `multi_to_terminal_color`, `diff_to_markdown`, `multi_to_markdown`.

### Changed

- MSRV remains `1.85`. No schema changes; `schema_version` stays at `1`.

### Fixed

- Broken intra-doc link `[`alloc`]` referenced from the crate-level docstring of dev-bench would warn under `cargo doc` when the `alloc-tracking` feature is disabled. The link is now a plain code span. Mirrors the same fix in dev-async, dev-stress, dev-chaos.

[0.9.1]: https://github.com/jamesgober/dev-report/releases/tag/v0.9.1

## [0.9.0] - 2026-05-08

### Added

#### CheckResult: tags and structured evidence

- `tags: Vec<String>` on `CheckResult` for category filtering. Defaults to empty.
- `evidence: Vec<Evidence>` on `CheckResult` for structured backing data. Defaults to empty.
- `Evidence` struct with four payload variants via `EvidenceData`:
  - `Numeric(f64)` for single labeled measurements.
  - `KeyValue(BTreeMap<String, String>)` for environment/config dumps (deterministic JSON ordering).
  - `Snippet(String)` for short text captures.
  - `FileRef(FileRef)` for source pointers, optionally with a `[line_start, line_end]` range.
- `EvidenceKind` enum returned by `Evidence::kind` for matching on payload shape without inspecting `data`.
- `FileRef` struct with `new` and `with_line_range` builders.
- `Evidence` constructors: `numeric`, `kv`, `snippet`, `file_ref`, `file_ref_lines`.
- `CheckResult` builders: `with_severity`, `with_tag`, `with_tags`, `with_evidence`, `with_evidences`.
- `CheckResult::has_tag(&str) -> bool`.
- `Report::checks_with_tag(&str)` iterator filter.

#### Output formats (opt-in features)

- `terminal` feature: `Report::to_terminal` (monochrome) and `Report::to_terminal_color` (ANSI-colored) pretty-printers. 80-column friendly. No new dependencies.
- `markdown` feature: `Report::to_markdown` exports a CommonMark-compatible Markdown document preserving every fact in the report. No new dependencies.

#### Aggregation and diffing

- `Report::diff(&baseline) -> Diff` for default-options diffing (20% duration regression threshold).
- `Report::diff_with(&baseline, &DiffOptions) -> Diff` for custom diffing thresholds.
- `Diff` struct surfacing: `newly_failing`, `newly_passing`, `severity_changes`, `duration_regressions`, `added`, `removed`. All vectors sorted alphabetically for determinism.
- `Diff::is_clean()` shortcut.
- `DiffOptions` with `duration_regression_pct` and `duration_regression_abs_ms` thresholds (either or both).
- `SeverityChange` and `DurationRegression` as serde-roundtrippable detail types.
- `MultiReport` aggregating multiple reports from different producers in a single CI run. Check identity is `(producer, name)`; same name across producers is kept separate. `MultiReport::overall_verdict` follows the same precedence as `Report::overall_verdict`.

#### Tests

- 14 explicit verdict-precedence transition-edge tests (DIRECTIVES § 7).
- v0.1.0 JSON deserialization regression test confirming backward compat.
- Round-trip tests for tags + evidence + diff.
- Pure-function tests for terminal and markdown renderers (same input → same output).
- 80-column-fit assertion on the terminal renderer for a typical report.

### Documentation

- REPS.md expanded: `CheckResult` fields, the full `Evidence` / `EvidenceData` / `EvidenceKind` / `FileRef` contract, the `terminal` and `markdown` feature contracts, and the `Diff` / `MultiReport` contracts.
- All public items now carry rustdoc examples (DIRECTIVES § 4 SHOULD-rule).

### Schema compatibility

- `schema_version` stays at `1`. All additions are purely additive.
- v0.1.0 JSON deserializes as a v0.9.0 `Report` with empty `tags` and `evidence` on every check.
- Empty `tags` and `evidence` are omitted on serialization, so a v0.9.0 report carrying neither serializes byte-equivalent to a v0.1.0-shaped JSON document.

[0.9.0]: https://github.com/jamesgober/dev-report/releases/tag/v0.9.0

## [0.1.0] - 2026-05-07

### Added

- Initial crate skeleton.
- `Verdict` enum: `Pass`, `Fail`, `Warn`, `Skip`.
- `Severity` enum: `Info`, `Warning`, `Error`, `Critical`.
- `CheckResult` struct with builder-style helpers (`pass`, `fail`, `warn`, `skip`, `with_detail`, `with_duration_ms`).
- `Report` struct with `push`, `finish`, `overall_verdict`, JSON round-trip.
- `Producer` trait for harness integration.
- Schema version field (`schema_version: u32`) on the report. Currently `1`.
- Round-trip and empty-report unit tests.

### Note

This is a name-claim release. The schema MAY change in subsequent `0.x` versions
as the rest of the `dev-*` suite is built and the contract gets exercised.

[Unreleased]: https://github.com/jamesgober/dev-report/compare/v0.9.3...HEAD
[0.1.0]: https://github.com/jamesgober/dev-report/releases/tag/v0.1.0