feat: cargo_diag backend (BuildDigest, rustc/cargo build diagnostics) - #16
Merged
Conversation
…ics)
Digests SAVED `cargo build --message-format=json` NDJSON streams: units are
CRATES (one per distinct package_id across compiler-artifact AND
compiler-message records — a crate that failed to compile emits messages but
no artifact and must still be a unit), named name-at-version parsed from
package_id.
package_id parsing was verified against a real cargo 1.94 capture before
writing the parser: modern cargo emits the Package ID Spec URL
(path+file:///ws/mathlib#0.1.0, bare-version fragment when the crate is named
like its directory; name-at-version fragment otherwise), NOT the legacy
'name version (source)' string — both shapes are handled, plus git PURLs
(?rev= query, .git suffix), with an addressable raw-id fallback for unknown
shapes.
The honesty story of this backend: cargo's stream carries NO timing, so
duration_us is None for EVERY unit (summarize_report falls back to file-order
ranking, and no coverage keys are fabricated). The diagnostic counts
(errors/warnings/notes_helps) are the opposite case: genuine 0.0 for a crate
that compiled clean, because the stream is a complete enumeration (the ptxas
'Used ...' rule). Verified live: a fully cached rebuild (fresh=true) replays
cached warnings, so the enumeration stays complete even when nothing
recompiles.
expand() carries the WHERE story without diagnostic text (translator, not
judge): per-level-per-file counts ('warning@mathlib/src/lib.rs' -> 2.0),
per-code counts ('code:E0308' -> 1.0), and the report-level
build_finished_success bool on every unit (the digest schema has no
report-level slot and core stays unbent).
New domain constant DOMAIN_BUILD_DIAG = "build_diag" in core/metrics.py;
one import line in server/app.py; one PROFILER_TOOLS entry ("cargo").
Fixtures are real cargo 1.94.0 captures from a tiny 2-crate workspace
(committed for provenance at tests/fixtures/cargo_diag_ws/, error variant
included): a warnings build (2 real warnings, success=true) and a
post-cargo-clean error build (real E0308 + failure-note, success=false),
captured from a neutral /tmp/perfdigest-ws root so the absolute paths cargo
unavoidably embeds in package_id carry no host-identifying information;
span file_names are workspace-relative (verified).
163 tests pass (149 baseline + 14 new), 16 hardware-gated skips unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
cargo_diag, a BuildDigest backend for SAVEDcargo build --message-format=jsonoutput (NDJSON): per-crate error/warning digests answering "where do this Rust
build's diagnostics concentrate" without spending agent context on rendered
compiler output. Formats
cargo-diag+ aliascargo-json; suffixes.cargo-diag.jsonl+.jsonl; new domain constantDOMAIN_BUILD_DIAG = "build_diag"incore/metrics.py.Design decisions
package_idseen acrosscompiler-artifactANDcompiler-messagerecords, in first-appearanceorder — a crate that FAILED to compile emits messages but never an artifact
and must still be a unit (the error fixture pins exactly this:
the bin crate has
artifacts == 0but is fully addressable).package_idparsing verified against the real capture before writing theparser (it changed across cargo versions). cargo 1.94 emits the Package ID
Spec URL:
path+file:///ws/mathlib#0.1.0— the fragment is a bare versionwhen the crate is named like its directory,
name@versionotherwise(registry PURLs). The parser handles the modern URL shape (bare-version and
name-at-version fragments, git
?rev=queries,.gitsuffixes), the legacyname version (source)string, and falls back to the raw id (addressable,never crashing, never inventing a name) on unknown shapes. Unit names are
short
name@version(e.g.mathlib@0.1.0).duration_usisNonefor EVERYunit. cargo's JSON stream carries no timing whatsoever, so this is a whole
backend whose duration is honestly absent —
summarize_reportranks by itsexisting file-order fallback (tested:
sorted_by == "file_order", and nototal_duration_us/coverage keys are fabricated), andget_metricsreturns
not_available_in_this_exportforduration_usend-to-end. Theusage prompt redirects build TIMING questions to
ninja-log/clang-time-trace.Used ...line, the message stream is a complete enumeration of thediagnostics this build emitted, so a crate that appears in the stream with
no messages has MEASURED zeros. Verified live before shipping the claim:
a fully cached rebuild (
fresh=trueartifacts) replays cached warnings ascompiler-messagerecords, so the enumeration stays complete even whennothing recompiles (this became a probe note).
errors/warnings/notes_helps(levels partitioned as: starts-witherrorincluding ICEs;warning; everything else —note/help/rustc'sspan-less
failure-note).expand()carries the WHERE story numerically:per-level-per-file keys built from the primary span's workspace-relative
file_name(warning@mathlib/src/lib.rs→ 2.0, span-less diags in anexplicit
(no-span)bucket), per-code keys (code:E0308→ 1.0,code:dead_code→ 1.0), plusartifacts/fresh_artifactsand the rawpackage_id. Reading diagnostic TEXT is the agent's editor/compiler job —the usage prompt says exactly that.
build-finished.success: the digest schema has no report-level slot andcore stays unbent, so the report-level outcome rides
expand()as abuild_finished_successbool on every unit — absent entirely (neverfabricated) if the capture was truncated before the
build-finishedrecord.multi-line document — its first line alone fails), not an object, or an
object without cargo's
reasonkey raises a namedValueError, with aredirect hint for the trap case (a perf-stat export is ALSO JSON-lines —
format
perf-stat-json).server/app.py::_register_backends(), ONEdomain constant in
core/metrics.py, onePROFILER_TOOLSentry(
"cargo_diag": "cargo"— same executable as criterion, different artifact).Rebased over the criterion (Add criterion backend (Rust benchmark digest, directory-ref reports) #14) and prev-green (Prove previous-green CI comparison on real two-run artifacts (CIDigest) #15) merges; no
format/suffix collisions (criterion claims
criterion/criterion-jsonwith
report_is_directory=True; cargo_diag is a plain file backend).Fixture provenance
Real cargo 1.94.0 captures, not hand-fabricated. The 2-crate workspace is
committed for provenance at
tests/fixtures/cargo_diag_ws/(mathliblibcrate with deliberate
unused_variables+dead_codewarnings;mathappbin crate depending on it — clean
main.rsplus themain.rs.error-variantthat produced the error fixture):
cargo_build_warnings_sample.cargo-diag.jsonl— fresh build of the cleanworkspace: 2 real warnings on
mathlib,mathappcompiles clean,build-finishedsuccess=true.cargo_build_error_sample.cargo-diag.jsonl— post-cargo cleanbuild afterintroducing a real E0308 (calling
add(2, "three")):mathlib's warnings,mathapp's error + rustc'sfailure-note, success=false, and nocompiler-artifactfor the failed crate.Host-identity check: cargo unavoidably embeds the absolute workspace path in
package_id/manifest_path/artifactfilenames, so the capture was run froma neutral
/tmp/perfdigest-wsroot — the committed NDJSON contains nohostname, username, or home path (grep-verified); nothing was hand-edited.
Span
file_names are workspace-relative (cargo run from the workspace root —verified), which is what the per-file concentration keys are built from.
Test evidence
New
tests/test_cargo_diag.py, 14 tests: registry dispatch on both formats;crate units named from real
package_ids; duration-None honesty end-to-end(
not_available_in_this_exportthroughget_metrics, all-None inlist_kernels); genuine-0.0 for the clean crate; real error/warning countsfrom both fixtures; summarize file-order fallback with no fabricated coverage
keys; expand concentration keys (per-level@file,
(no-span)bucket, per-code)build_finished_successvia expand(true and false fixtures); failed-crate-has-no-artifact; list/expand index
agreement; synthetic (clearly marked)
package_idshape pinning forregistry/git/legacy variants the path-dependency fixture cannot produce; loud
ValueErrors on the three non-NDJSON traps; vocabulary hint for foreign terms.Full suite:
uv sync --extra dev && uv run --extra dev pytest -q→182 passed, 16 skipped (168 post-#14/#15 baseline + 14 new;
hardware-gated skip count unchanged).
Test plan
uv run pytest -q— 182 passed, 16 skipped (after rebase onto a82e7fd)toolchain before it shipped as a probe note
platform_capabilities()(digest + capture rows) andsuggest_profile_command('cargo_diag', ...)on this host🤖 Generated with Claude Code