feat(cli,format): deterministic JSON envelope with plumb_version, run_id, summary - #123
Conversation
…_id, summary Wraps the JSON formatter output in an envelope object with `plumb_version`, a content-derived `run_id`, a `summary` stats block, and the sorted `violations` array. The `run_id` is `sha256:<hex>` of the compact serialization of the sorted violations, so whitespace tweaks and version bumps never shift the digest while any observable change in a violation flips it. Adds a `tests/determinism.rs` suite that locks in byte-identical output across runs for every formatter, covers the envelope shape, and asserts that re-ordering inputs leaves `run_id` unchanged. Closes #31. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Now I have everything I need for a complete review. Here is my full assessment: Review:
|
| # | File:Line | Severity | Issue |
|---|---|---|---|
| 1 | tests/determinism.rs:84-99 |
Warning | json_run_id_is_stable_under_input_reordering silently no-ops forever because fixture() yields 1 violation; the reordering invariant is never asserted |
| 2 | tests/snapshots/format_snapshots__json.snap:6 |
Warning | plumb_version is a volatile field locked in a golden snap; will fail CI on every routine version bump |
Verdict: REQUEST_CHANGES
Summary
Closes #31. Wraps
plumb lint --format jsonoutput in a deterministic envelope:{ "plumb_version": "0.0.1", "run_id": "sha256:<hex>", "summary": { "error": N, "info": N, "total": N, "warning": N }, "violations": [ ... ] }plumb_version— compile-timeenv!("CARGO_PKG_VERSION")ofplumb-format.run_id—sha256:<hex>of the compact serialization of the sorted violations array. Whitespace and version bumps don't shift it; any observable violation change flips it.summary— counts by severity, alphabetically keyed.violations— defensively re-sorted byViolation::sort_key()before hashing and emit.json()builds the envelope with explicit alphabetical inserts so output stays stable regardless of whetherserde_json/preserve_orderis toggled in the workspace (currently enabled transitively viaschemars).Acceptance criteria
just determinism-checkand a newtests/determinism.rssuite (per-formatter byte-identity tests forjson/pretty/sarif/mcp_compact).run_idderivation documented — rustdoc onjson()describes the hash input precisely.plumb-output.schema.json— the schema file does not yet exist in-tree; this PR ships the envelope shape such a schema would describe. Flagged for a follow-up issue.Test plan
cargo nextest run -p plumb-formatcargo nextest run -p plumb-cli(existing CLI integration JSON test still finds"rule_id"insideviolations[])cargo clippy --workspace --all-targets -- -Dwarningscargo fmt --checkjust determinism-checkjust validateend-to-end🤖 Generated with Claude Code