{"id":49199391,"url":"https://github.com/asmuelle/cargo-impact","last_synced_at":"2026-05-02T10:00:30.377Z","repository":{"id":353081285,"uuid":"1217860366","full_name":"asmuelle/cargo-impact","owner":"asmuelle","description":"Cargo Impact","archived":false,"fork":false,"pushed_at":"2026-05-02T07:59:14.000Z","size":407,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-02T09:37:41.244Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/asmuelle.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-22T09:38:58.000Z","updated_at":"2026-05-02T07:59:11.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/asmuelle/cargo-impact","commit_stats":null,"previous_names":["asmuelle/cargo-impact"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/asmuelle/cargo-impact","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asmuelle%2Fcargo-impact","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asmuelle%2Fcargo-impact/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asmuelle%2Fcargo-impact/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asmuelle%2Fcargo-impact/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/asmuelle","download_url":"https://codeload.github.com/asmuelle/cargo-impact/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asmuelle%2Fcargo-impact/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32530176,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T01:12:54.858Z","status":"online","status_checked_at":"2026-05-02T02:00:05.923Z","response_time":132,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-23T13:01:37.805Z","updated_at":"2026-05-02T10:00:30.351Z","avatar_url":"https://github.com/asmuelle.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cargo-impact\n*Predictive Regression Analysis \u0026 Verification Mapping for Rust*\n\n[![CI](https://github.com/asmuelle/cargo-impact/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/asmuelle/cargo-impact/actions/workflows/ci.yml)\n[![Security](https://github.com/asmuelle/cargo-impact/actions/workflows/security.yml/badge.svg?branch=main)](https://github.com/asmuelle/cargo-impact/actions/workflows/security.yml)\n[![Spec](https://github.com/asmuelle/cargo-impact/actions/workflows/spec.yml/badge.svg?branch=main)](https://github.com/asmuelle/cargo-impact/actions/workflows/spec.yml)\n[![Release](https://github.com/asmuelle/cargo-impact/actions/workflows/release.yml/badge.svg)](https://github.com/asmuelle/cargo-impact/actions/workflows/release.yml)\n[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license)\n\n\u003e **Status:** v0.4.0 (stable) on [crates.io](https://crates.io/crates/cargo-impact) — `cargo install cargo-impact`. This README is both the living design spec and the user manual; sections describing yet-unshipped behavior are explicitly called out (§11 has the full shipped-vs-deferred breakdown).\n\n## Contents\n\n1. [Core Philosophy](#1-core-philosophy)\n2. [Quickstart (Intended UX)](#2-quickstart-intended-ux)\n3. [Technical Architecture](#3-technical-architecture)\n4. [CLI Interface (UX)](#4-cli-interface-ux)\n5. [Vibe Coding Workflow Integration](#5-vibe-coding-workflow-integration)\n6. [Summary Table: Context vs. Impact](#6-summary-table-context-vs-impact)\n7. [Integration with `cargo-context`](#7-integration-with-cargo-context)\n8. [MCP Server Surface](#8-mcp-server-surface)\n9. [Performance Targets](#9-performance-targets)\n10. [Non-Goals](#10-non-goals)\n11. [Implementation Roadmap](#11-implementation-roadmap)\n12. [License](#license)\n\n---\n\n## 1. Core Philosophy\n`cargo-impact` moves the developer from \"Running all tests and hoping for the best\" to **Surgical Verification**. It treats a code change as a \"stone thrown into a pond\" and calculates exactly which ripples hit which shores (tests, docs, APIs).\n\nIt answers the critical question: *\"I changed X; what is the minimum set of things I must check, and how confident can I be in each one?\"* Every finding is labeled with a confidence tier — static analysis is never certain, and the tool is honest about that.\n\n---\n\n## 2. Quickstart\n\n### Install\n\nPick whichever path fits your environment:\n\n```bash\n# 1. crates.io\ncargo install cargo-impact\n\n# 2. Pinned from source by tag (works today, no crates.io dependency)\ncargo install --git https://github.com/asmuelle/cargo-impact --tag v0.4.0\n\n# 3. Prebuilt binary from the GitHub release page\n#    https://github.com/asmuelle/cargo-impact/releases\n#    Binaries for linux-x86_64, linux-aarch64, macos-x86_64,\n#    macos-aarch64, and windows-x86_64 are attached to each release.\n```\n\n### In CI — [`cargo-impact-action`](https://github.com/asmuelle/cargo-impact-action)\n\nDrop `cargo-impact` into a GitHub repo with ≤10 lines of YAML. The\naction installs cargo-impact, runs against the PR diff, uploads a\nSARIF report (code scanning renders findings inline on the diff),\nand posts a sticky markdown PR comment.\n\n```yaml\non: [pull_request]\npermissions:\n  contents: read\n  pull-requests: write\n  security-events: write\njobs:\n  impact:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with: { fetch-depth: 0 }\n      - uses: asmuelle/cargo-impact-action@v1\n        with:\n          fail-on: high      # omit to stay informational-only\n```\n\nSee the [action's README](https://github.com/asmuelle/cargo-impact-action)\nfor all inputs/outputs and troubleshooting.\n\n### First run\n\n```bash\ncd my-rust-project\ncargo impact\n```\n\nExpected first-run output on a clean workspace:\n\n```text\ncargo-impact: no Rust files changed relative to HEAD\n```\n\nMake a change, re-run, and you'll get the severity-grouped report. `cargo impact --help` lists every flag.\n\n### Typical AI loop\n```bash\ncargo impact --context \\               # 1. Feed cargo-context just the\n  | cargo context --files-from -       #    files inside the blast radius\n# ... give the pack to your AI, have it generate a patch, apply it ...\ncargo impact --format markdown         # 2. Get the verification checklist\n# ... AI ticks items, flags what it cannot verify ...\ncargo impact --test                    # 3. Run only the affected tests\n```\n\n(Without `cargo-context` installed, `cargo impact --context | xargs cat` is a\nplain-text fallback — see §7 for the full integration story.)\n\nFor agent-native consumption, start the MCP server: `cargo impact mcp` speaks JSON-RPC 2.0 over stdio with all six tools from §8.\n\n### Working *on* this repo with Claude Code\n\nIf you clone this repo to contribute and open it in Claude Code, you're set up automatically. The committed `.mcp.json` registers both `cargo-impact` and `cargo-context` as MCP servers; `.claude/settings.json` enables them via an explicit allowlist and pre-approves the relevant `cargo impact` / `cargo context` Bash patterns so agents don't hit permission prompts. `CLAUDE.md` at the repo root orients assistants to the project's conventions (MSRV 1.95, edition 2024, honest tiering, the three-check pre-commit gate).\n\nInstall both tools from crates.io first, then open the repo:\n\n```bash\ncargo install cargo-impact cargo-context\ncd cargo-impact\nclaude   # or code . / zed . with Claude Code configured\n```\n\nMissing either binary degrades gracefully — the MCP runtime logs the unavailable server and skips its tools; nothing else breaks.\n\n---\n\n## 3. Technical Architecture\n\n`cargo-impact` is an **orchestrator**, not a from-scratch analyzer. It composes existing best-in-class Rust tooling into a single blast-radius report, with every finding labeled by confidence tier rather than a binary \"affected/not affected\" flag.\n\n### A. Analysis Backend (The Engine)\nStatic analysis of Rust requires *resolved names*, not just syntax. The backend is layered:\n\n| Layer | Tool | Responsibility |\n| :--- | :--- | :--- |\n| Syntax | `syn` | Parse diff hunks → candidate symbols |\n| Macro expansion | `cargo expand` / HIR | Expand derives, attribute, and `fn`-like macros before analysis (critical for `serde`, `axum`, `clap`, `tokio`) |\n| Name resolution \u0026 call graph | `rust-analyzer` as library (`ra_ap_hir`, `ra_ap_ide`) | Resolve paths, find references, traverse calls across modules and crates |\n| Public API | `rustdoc --output-format json` + `cargo-public-api` | Stable diff of the public surface |\n| Semver impact | `cargo-semver-checks` | Classify public changes as additive vs. breaking |\n| Cache | `target/impact-cache/` keyed by content hash + cargo fingerprint | Sub-second warm runs; incremental invalidation |\n\nWhy not `syn` alone: re-exports, trait method dispatch, generics, and macro-generated code all defeat syntax-only analysis. rust-analyzer gives IDE-grade precision on stable Rust.\n\n### B. Blast Radius Detection\nFor each changed symbol, `cargo-impact` emits a finding with a **confidence tier** (see §3F):\n\n*   **Direct references:** Resolved call-graph edges from the reverse-reference index → `Proven`.\n*   **Trait ripple (differentiated):**\n    *   Required method signature changed → `Proven` (all impls break at compile time; report as build consequence, not risk).\n    *   Default method body changed → `Likely` for impls that *don't* override; `Proven` for impls that delegate via `super::`.\n    *   New method added → `Proven` compile break unless defaulted.\n    *   Trait bound changed → `Likely` for downstream generic code.\n*   **Dynamic dispatch (`dyn Trait`):** All impls reachable via `dyn Trait` construction sites → `Likely`.\n*   **FFI / `unsafe extern`:** Any change to `extern \"C\"` signatures or `#[no_mangle]` symbols → `Proven` HIGH (blast radius leaves Rust entirely).\n*   **`build.rs` changes:** Treated as `Proven` HIGH by default — build scripts can invalidate downstream compilation in non-obvious ways.\n*   **Feature-gated code:** If the changed symbol is behind `#[cfg(feature = \"...\")]`, run analysis per active feature set. `--features`, `--all-features`, and `--feature-powerset` supported.\n\n### C. Surgical Testing\nDoes not re-implement test selection — orchestrates proven tools:\n\n*   **Coverage-driven selection:** Delegates to [`cargo-difftests`](https://github.com/dnbln/cargo-difftests) for file-to-test mapping based on actual coverage traces.\n*   **Call-graph augmentation:** Adds tests that *statically* reference changed symbols but weren't hit by the last coverage run (catches untested paths).\n*   **Emits nextest filters:** Output is a [`cargo-nextest`](https://nexte.st) filter expression, e.g. `cargo nextest run -E 'test(test_auth_login) + test(test_api_handshake)'`. Falls back to `cargo test` filters when nextest is absent.\n*   **Handles:** doctests in triple-backtick blocks inside doc comments, `#[cfg(test)] mod tests`, per-member integration tests in workspaces, `rstest` / `proptest` parameterization, `#[ignore]`, `serial_test`.\n\n### D. Runtime Surface Mapping (The \"Exposed\" Layer)\nFramework detection runs **after macro expansion** (handler attributes, derive-based routers are invisible pre-expansion). Pluggable adapters, not hardcoded framework logic:\n\n*   **HTTP routers:** `axum` (`Router::route`, nested routers, `#[debug_handler]`), `actix-web` (`web::resource`, scopes), `rocket` (`#[get]`/`#[post]`), `warp`.\n*   **CLI:** `clap` derive and builder APIs.\n*   **Desktop/mobile:** `tauri` commands, `dioxus` routes.\n*   **Public crate API:** Delegated to `cargo-semver-checks` + `cargo-public-api`.\n*   **Adapter contract:** A small trait so third parties can register custom surface mappers for proprietary frameworks.\n\n### E. Documentation Drift Detection\nPrecise, not keyword-based:\n\n*   **Intra-doc links:** Parses rustdoc JSON for `[\\`PaymentGateway\\`]`-style references in doc comments and `/docs/*.md`. Exact symbol resolution, not substring match on `User`.\n*   **Rustdoc example blocks:** Flags doctest examples that exercise changed symbols.\n*   **Changelog heuristic:** If a `pub` item changed and `CHANGELOG.md` / `RELEASES.md` wasn't touched in the same diff → flag.\n\n### F. Confidence Tiers\nEvery finding carries a tier. This replaces the fiction that static analysis is ever \"99% sure.\"\n\n| Tier | Score | Meaning | Example source |\n| :--- | :--- | :--- | :--- |\n| **Proven** | 0.95–1.00 | Resolved call-graph edge or rustdoc JSON symbol match | `fn a()` calls `fn b()` directly, both resolved |\n| **Likely** | 0.60–0.94 | Trait impl via `dyn`, feature-gated caller, default-method non-overrider | Handler registered via `axum::Router::route` with expanded macro |\n| **Possible** | 0.30–0.59 | Heuristic match, unexpanded macro residue, cross-crate without rustdoc | Identifier appears in doc comment without intra-doc link |\n| **Unknown** | \u003c 0.30 | Listed but not scored | Reflection via `Any`, runtime config, FFI callbacks, `OnceCell` mutation |\n\n`--confidence-min=0.6` filters output for CI; default shows all tiers with the score attached.\n\n---\n\n## 4. CLI Interface (UX)\n\nFlags below reflect the shipping v0.4.0 surface. `--checklist` remains embedded inside `--format markdown` rather than a dedicated output mode; see §11 for the full shipped-vs-deferred breakdown.\n\n```bash\n# Analyze the current working tree against HEAD\ncargo impact\n\n# Emit a cargo-nextest filter expression for affected tests only\ncargo impact --test\n# Example output: test(auth_roundtrip) + test(api_smoke)\n\n# Analyze against a specific revision (branch, tag, SHA)\ncargo impact --since main\n\n# AI-consumable formats\ncargo impact --format text       # default — severity-grouped text with emoji icons\ncargo impact --format markdown   # summary + per-severity sections + verification checklist\ncargo impact --format json       # structured envelope; stable schema for agents\ncargo impact --format sarif      # SARIF v2.1.0 for code-scanning uploads\ncargo impact --format pr-comment # compact GitHub PR comment markdown\n\n# CI gating\ncargo impact --confidence-min 0.6   # hide Possible / Unknown findings\ncargo impact --fail-on high         # exit 1 if any HIGH finding is emitted\ncargo impact --fail-on medium       # exit 1 on HIGH or MEDIUM\n\n# Feature-aware analysis — cfg(feature = \"x\") gates are evaluated against\n# the resolved active set. Items whose gates don't match are stripped before\n# every analyzer sees them.\ncargo impact --features tokio,rt            # union with default features\ncargo impact --features tokio --no-default-features\ncargo impact --all-features                 # audit the full feature surface\ncargo impact --feature-powerset             # baseline + no-default + all-features\n\n# Opt-in macro expansion (requires cargo-expand on PATH; graceful no-op if absent).\ncargo impact --macro-expand\n\n# Opt-in public-API breakage detection (requires cargo-semver-checks on PATH;\n# runs rustdoc twice internally, typically 10–30s).\ncargo impact --semver-checks\n```\n\n### Sample report (text)\n\nv0.2 is syn-only; no finding reaches the `Proven` tier — that is reserved for resolved call-graph analysis arriving with rust-analyzer in v0.3. Every score below is the *honest* ceiling for syntactic analysis.\n\n```text\ncargo-impact v0.2.0\n\nChanged files (3):\n  src/engine.rs\n  src/ffi.rs\n  build.rs\n\nCandidate symbols (4):\n  Greeter\n  callback_t\n  process_event\n  UserProfile\n\n🔴 HIGH (3)\n  [f-0001] build.rs changed (build.rs) · Likely 0.90\n  [f-0002] FFI callback_t modified in src/ffi.rs · Likely 0.95\n  [f-0003] impl Greeter for Foo (src/engine.rs) · Likely 0.80\n\n🟡 MEDIUM (2)\n  [f-0004] test `api_smoke` (tests/integration.rs) references process_event · Likely 0.85\n  [f-0005] dyn Greeter used in src/dispatch.rs · Likely 0.75\n\n🔵 LOW (1)\n  [f-0006] intra-doc link to UserProfile in docs/architecture.md:42 · Likely 0.90\n\n⚪ UNKNOWN (0)\n```\n\n### Sample JSON envelope\n\nStable across releases; matches the MCP tool-call schema (§8) so the CLI and the future MCP server return identical shapes.\n\n```json\n{\n  \"version\": \"0.2.0\",\n  \"changed_files\": [\"src/engine.rs\", \"src/ffi.rs\", \"build.rs\"],\n  \"candidate_symbols\": [\"Greeter\", \"UserProfile\", \"callback_t\", \"process_event\"],\n  \"findings\": [\n    {\n      \"id\": \"f-0001\",\n      \"severity\": \"high\",\n      \"tier\": \"likely\",\n      \"confidence\": 0.9,\n      \"kind\": \"build_script_changed\",\n      \"file\": \"build.rs\",\n      \"evidence\": \"build script `build.rs` changed — build scripts can invalidate downstream compilation in non-obvious ways (…)\"\n    },\n    {\n      \"id\": \"f-0004\",\n      \"severity\": \"medium\",\n      \"tier\": \"likely\",\n      \"confidence\": 0.85,\n      \"kind\": \"test_reference\",\n      \"test\": { \"file\": \"tests/integration.rs\", \"symbol\": \"api_smoke\" },\n      \"matched_symbols\": [\"process_event\"],\n      \"evidence\": \"test body references process_event (syntactic match, no name resolution)\",\n      \"suggested_action\": \"cargo nextest run -E 'test(api_smoke)'\"\n    }\n  ],\n  \"summary\": {\n    \"total\": 6,\n    \"by_severity\": { \"high\": 3, \"medium\": 2, \"low\": 1 },\n    \"by_tier\":     { \"proven\": 0, \"likely\": 6, \"possible\": 0, \"unknown\": 0 }\n  }\n}\n```\n\n### Markdown output\n\n`--format markdown` produces a paste-to-AI-ready document: summary, per-severity sections, and a verification checklist with `- [ ]` items an agent can tick. Example shape:\n\n```markdown\n# cargo-impact v0.2.0 blast radius\n\n## Summary\n- **Changed files:** 3\n- **Candidate symbols:** 4\n- **Findings:** 6 (3 high, 2 medium, 1 low, 0 unknown)\n\n## 🔴 HIGH (3)\n- **[f-0001]** `build.rs` changed (build.rs) — *Likely 0.90* — build script changed …\n- **[f-0002]** FFI `callback_t` modified in src/ffi.rs — *Likely 0.95* — blast radius leaves Rust …\n…\n\n## Verification checklist\n- [ ] **HIGH** `build.rs` changed — *Likely 0.90*\n- [ ] **HIGH** FFI `callback_t` modified in src/ffi.rs — *Likely 0.95*\n- [ ] **MEDIUM** test `api_smoke` references process_event — *Likely 0.85*\n…\n```\n\n---\n\n## 5. Vibe Coding Workflow Integration\n\nThis completes the **Context → Code → Verify** loop:\n\n1.  **Context:** `cargo context --fix | pbcopy` → AI generates a fix.\n2.  **Apply:** Developer applies the AI's code.\n3.  **Impact:** Developer runs `cargo impact`.\n4.  **Verify:**\n    *   `cargo impact` flags a specific integration test and one API endpoint (with confidence tiers).\n    *   Developer runs `cargo impact --test` (5 seconds instead of 5 minutes).\n    *   Developer runs `cargo impact --checklist --format=markdown | pbcopy` and pastes back to the AI: *\"Here's the verification checklist — tick what you've addressed and tell me what you still need me to test manually.\"*\n\n## 6. Summary Table: Context vs. Impact\n\n| Feature | `cargo-context` (The Input) | `cargo-impact` (The Output) |\n| :--- | :--- | :--- |\n| **Goal** | Maximize AI understanding | Minimize human verification effort |\n| **Focus** | What is the AI looking at? | What did the AI touch? |\n| **Primary Tools** | `git diff` + `cargo metadata` + rustdoc JSON | `rust-analyzer` call graph + `cargo-difftests` + `cargo-semver-checks` |\n| **Key Output** | A Markdown Context Pack | A confidence-tiered Blast Radius Report |\n| **Vibe Shift** | No more copy-pasting files | No more \"test all and pray\" |\n\n---\n\n## 7. Integration with `cargo-context`\n\n`cargo-impact` and `cargo-context` are designed as a **bidirectional pair**, not two isolated tools. They share cache, symbol index, and MCP surface.\n\n### Forward flow: Context → Impact\nThe normal developer loop. AI edits files inside a context pack; `cargo-impact` analyzes the resulting diff.\n\n```bash\ncargo context --fix | pbcopy         # AI gets context, generates patch\n# ... apply patch ...\ncargo impact                         # analyze what the patch touched\n```\n\n### Reverse flow: Impact → Context ✅ shipped\n`cargo-impact --context` emits a deduped, newline-delimited list of every file implicated in the blast radius (changed files + each finding's primary path). `cargo-context --files-from -` consumes that list directly and builds a context pack scoped to exactly those files:\n\n```bash\ncargo impact --context \\\n  | cargo context --files-from - \\\n  | pbcopy\n# Pack contains only the blast-radius files, not the whole repo.\n```\n\n`cargo-context` applies its usual scrubber to each path (so `.env` etc. never leak raw secrets), skips missing paths with an accounting header, and prioritizes the scoped section at diff-level priority so it survives `--budget` pressure. Implementation: [cargo-context#5](https://github.com/asmuelle/cargo-context/issues/5).\n\n### Scope-limited context packs ⏳ not scheduled\nPassing the full `cargo-impact` JSON envelope (not just file paths) so `cargo-context` can prioritize by confidence tier, filter out findings already verified elsewhere, or emit per-finding mini-packs. A schema proposal is on record at [cargo-context#5](https://github.com/asmuelle/cargo-context/issues/5#issuecomment-4304409079), but the issue closed without acceptance and no concrete implementation is tracked on either side. Users who need this today can pipe `--format=json` into their own tooling; the envelope shape is stable within v0.3.\n\n```bash\n# If/when the cargo-context side lands:\ncargo impact --format=json \u003e .impact.json\ncargo context --impact-scope=.impact.json   # per-finding packs\n```\n\n### Shared cache ⏳ deferred\nThe spec imagined both tools reading `target/ai-tools-cache/` with namespaced subdirectories (`context/`, `impact/`) so rust-analyzer's index is built once and shared. Neither tool ships this yet; each maintains its own cache. A real implementation needs the cache format versioned independently of both tools — tracked for a joint v0.4.\n\n### Shared MCP server ⏳ deferred\nCombined `cargo-ai-tools` MCP server that exposes both families under one process (see §8 for the cargo-impact side). Also v0.4+.\n\n---\n\n## 8. MCP Server Surface\n\n`cargo-impact` ships as a CLI **and** an MCP server. Agents (Claude Code, Cursor, Zed, custom) connect over stdio and call tools directly — no copy-paste, no shell parsing.\n\n```bash\ncargo impact mcp           # start MCP server over stdio\ncargo impact mcp --http    # or Streamable HTTP on localhost\n```\n\n### Tools exposed\n\n| Tool | Purpose | Returns |\n| :--- | :--- | :--- |\n| `impact.analyze` | Run blast radius on current diff or commit range | Structured finding graph with tiers |\n| `impact.test_filter` | Get a `cargo-nextest` filter expression for affected tests | String + rationale per test |\n| `impact.checklist` | Generate the verification checklist | Markdown + JSON sibling |\n| `impact.surface` | List affected runtime surfaces (routes, CLI subcommands, FFI) | Structured surface list |\n| `impact.semver` | Classify public API changes | `{additive, breaking, none}` + reasons |\n| `impact.explain` | Explain *why* a specific finding was flagged | Trace of the evidence chain |\n\n### Tool schema example: `impact.analyze`\n\n```json\n{\n  \"name\": \"impact.analyze\",\n  \"description\": \"Analyze the blast radius of a git diff in a Rust workspace.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"since\": { \"type\": \"string\", \"description\": \"Git ref, e.g. 'HEAD~1' or 'main'. Defaults to unstaged+staged.\" },\n      \"features\": { \"type\": \"array\", \"items\": { \"type\": \"string\" } },\n      \"all_features\": { \"type\": \"boolean\", \"default\": false },\n      \"confidence_min\": { \"type\": \"number\", \"minimum\": 0, \"maximum\": 1 },\n      \"max_findings\": { \"type\": \"integer\", \"default\": 200 }\n    }\n  }\n}\n```\n\n### Response envelope\nEvery tool returns the same outer shape so agents can reason uniformly:\n\n```json\n{\n  \"findings\": [\n    {\n      \"id\": \"f-0001\",\n      \"severity\": \"high|medium|low|unknown\",\n      \"tier\": \"proven|likely|possible|unknown\",\n      \"confidence\": 0.98,\n      \"kind\": \"direct_call|trait_impl|dyn_dispatch|ffi|route|doc_drift|semver\",\n      \"source\": { \"file\": \"src/core/engine.rs\", \"symbol\": \"process_event\", \"span\": [41, 67] },\n      \"target\": { \"file\": \"tests/integration_tests.rs\", \"symbol\": \"api_smoke\" },\n      \"evidence\": \"Resolved call-graph edge via ra_ap_ide::references\",\n      \"suggested_action\": \"cargo nextest run -E 'test(api_smoke)'\"\n    }\n  ],\n  \"summary\": { \"proven\": 4, \"likely\": 7, \"possible\": 2, \"unknown\": 1 },\n  \"cache\": { \"hit\": true, \"build_time_ms\": 142 }\n}\n```\n\n### Why not just parse CLI output\nAgents parsing pretty-printed CLI text is brittle and burns tokens. MCP tool calls return typed JSON, stream progress for long analyses, and let the agent ask `impact.explain(id=\"f-0007\")` to drill into a specific finding without re-running the whole analysis.\n\n---\n\n## 9. Performance Targets\n\nThe \"5 seconds, not 5 minutes\" claim in §5 needs teeth. These are the SLOs the tool targets — not aspirational, but used as regression thresholds in the benchmark suite.\n\n| Scenario | Target | Measured on |\n| :--- | :--- | :--- |\n| Warm run, \u003c50 changed files, small workspace (\u003c10 crates) | **\u003c 500ms** | `cargo-impact` self-hosting benchmark |\n| Warm run, typical workspace (10–30 crates) | **\u003c 1.5s** | `ripgrep`, `zola`-sized repos |\n| Warm run, large workspace (100+ crates) | **\u003c 5s** | `rustc`, `deno`, internal monorepos |\n| Cold run (first invocation, no cache) | **\u003c 30s** for 100-crate workspace | includes RA index build |\n| MCP response p95 | **\u003c 200ms** after warm cache | per-tool-call latency |\n| `--feature-powerset` on 8 features | **\u003c 60s** | CI-only mode, not interactive |\n\n### Cache strategy\n*   **Keyed on:** `(file_content_hash, cargo_fingerprint, rustc_version, features_hash)`.\n*   **Granularity:** Per-file symbol index, per-symbol call-graph edges. A one-line change in `src/utils.rs` invalidates only `utils`'s index and its dependents' edges — not the whole crate.\n*   **Location:** `target/ai-tools-cache/impact/` (shared with `cargo-context`, see §7).\n*   **Eviction:** LRU by access time; cap at 500MB by default, configurable via `CARGO_IMPACT_CACHE_SIZE`.\n*   **Invalidation signal:** `cargo-impact` watches `Cargo.lock` and rustc version; bumps purge dependent caches.\n\n### When performance degrades\nIf a run exceeds 2× the target for its scenario, `cargo-impact` emits a diagnostic:\n\n```\n⚠ cargo-impact: analysis took 4.2s (target \u003c 1.5s for this workspace size).\n  Likely cause: rust-analyzer index cold (cache dir recently cleared).\n  Subsequent runs should be fast. Run `cargo impact --bench` to profile.\n```\n\n### Benchmark suite\n`cargo impact --bench` runs a built-in benchmark against the current workspace, reports against the SLO table, and writes a JSON trace for regression tracking. Designed to be run in CI on the cargo-impact repo itself — no flaky wall-clock assertions, uses the cargo fingerprint to skip when inputs are unchanged.\n\n---\n\n## 10. Non-Goals\n\nScope discipline matters. `cargo-impact` is a **static impact oracle**, not a correctness checker. The following are explicitly out of scope and will not be added:\n\n*   **Runtime behavior verification.** The tool does not execute code, trace syscalls, or observe actual runtime paths. A function can pass every affected test and still be broken in production; `cargo-impact` cannot detect that.\n*   **Logic bug detection.** If the AI writes `a + b` where it meant `a - b` and the tests don't catch it, neither will `cargo-impact`. We tell you *what* to check, not *whether the logic is right*.\n*   **Code review replacement.** A human (or reviewing agent) still reads the diff. The blast radius tells them where to focus, not what to conclude.\n*   **Mutation testing.** That is [`cargo-mutants`](https://github.com/sourcefrog/cargo-mutants)'s job. If you want \"would this test catch a bug if one existed,\" use that.\n*   **Fuzzing / property testing integration.** Out of scope. Run `cargo-fuzz` / `proptest` separately and feed their failures back to the AI through whatever channel you already use.\n*   **Type-level refactoring guidance.** We flag that `UserProfile` was modified; we do not advise on whether a different type design would have been better.\n*   **Runtime tracing or profiling.** Not a flamegraph, not a tokio console, not a perf tool.\n*   **IDE diagnostics.** `rust-analyzer` already does this. We consume its index; we do not compete with it.\n*   **Formatting, linting, or style enforcement.** `rustfmt` and `clippy` exist.\n*   **Dependency vulnerability scanning.** That is [`cargo-audit`](https://rustsec.org) / [`cargo-deny`](https://github.com/EmbarkStudios/cargo-deny).\n*   **Build-time regression detection.** Changes that blow up compile time are real but invisible to us — use [`cargo-bloat`](https://github.com/RazrFalcon/cargo-bloat) or `-Z self-profile`.\n*   **Cross-language impact.** A Rust change that breaks a Python FFI consumer is flagged at the `extern \"C\"` boundary (§3B) but we do not trace into the foreign language.\n*   **Non-Rust workspaces.** We are `cargo-*`. Polyglot monorepos are out of scope; run `cargo-impact` against the Rust portion and compose with your own tooling for the rest.\n\n### What we *will* integrate but not reinvent\n*   Selective testing → `cargo-difftests`\n*   Semver classification → `cargo-semver-checks`\n*   Public-API diffing → `cargo-public-api`\n*   Test execution → `cargo-nextest`\n*   Name resolution → `rust-analyzer` (as library)\n\nIf an existing tool solves a subproblem well, we orchestrate it. If we find ourselves reimplementing one, that is a signal we are off-mission.\n\n---\n\n## 11. Implementation Roadmap\n\nThe spec is deliberately ambitious. These milestones are the cut points where the tool is genuinely useful to a real user, not a lab demo. Each milestone ships independently.\n\n### v0.1 — \"Surgical test filter\" (the MVP) ✅ shipped\n**Goal:** A Rust developer saves time on `cargo test` today, with zero AI integration.\n\n*   ✅ `cargo impact` parses `git diff` with `syn` → candidate symbols\n*   ⏭ `rust-analyzer` (as library) resolves direct call-graph references — deferred to v0.3; v0.2 uses syn-only token matching honestly tiered `Likely`\n*   ✅ Emits a `cargo-nextest` filter expression via `--test`\n*   ✅ Human-readable text output\n\n**Deliberately deferred:** macros, traits, features, MCP, framework adapters, confidence tiers, public-API analysis. If v0.1 isn't a 2-week project, we're over-engineering.\n\n**Success metric:** On the `ripgrep` workspace, typical edits trigger \u003c10% of tests with zero false negatives across 50 seeded changes.\n\n### v0.2 — \"Honest blast radius\" (in progress — core shipped)\n**Goal:** The report earns the name. Confidence tiers, trait handling, and the first AI-consumable format.\n\n*   ⚠ Macro expansion — `#[derive(...)]` now recognized (matched on last path segment, so `serde::Serialize` / `clap::Parser` / etc. all resolve). Full `cargo expand` / HIR pass for attribute and `fn`-like macros still deferred; nightly toolchain boundary\n*   ✅ Trait ripple differentiation (§3B): required vs. default vs. new method — per-method HEAD-vs-WT classification shipping in addition to the blanket `TraitImpl` scan\n*   ✅ `dyn Trait` dispatch edges as `Likely 0.75`\n*   ✅ Confidence tiers (§3F) with numeric scores (`Proven` reserved for v0.3 RA integration)\n*   ✅ `--format=json` and `--format=markdown`\n*   ✅ `--features` / `--all-features` / `--no-default-features` — cfg-aware AST filtering against the resolved feature set\n*   ✅ `cargo-semver-checks` integration (opt-in via `--semver-checks`)\n*   ✅ `--confidence-min` and `--fail-on={high,medium,low}` for CI\n*   ✅ Documentation drift via intra-doc links (plus length-gated keyword fallback)\n*   ✅ Bonus: diff-aware candidate symbols (HEAD-vs-WT per-item comparison) + FFI signature tracking + `build.rs` change detection\n\n**Success metric:** On the cargo-impact repo itself, 95% of `Proven`-tier findings correspond to tests that actually fail when the finding is seeded as a regression. *(v0.2 emits no `Proven` findings — the metric re-activates once rust-analyzer integration lands in v0.3.)*\n\n### v0.3 — \"Agent-native\"\n**Goal:** First-class AI integration. The tool is now consumed by agents, not just humans.\n\n*   ✅ MCP server (`cargo impact mcp`) — all six §8 tools (`impact_analyze`, `impact_test_filter`, `impact_surface`, `impact_semver`, `impact_explain`, `impact_version`) ship in v0.3.0.\n*   ✅ Rust-analyzer integration for the `Proven` tier — LSP stdio client with Content-Length framing, initialize handshake, indexing-progress wait, `documentSymbol` + `references` queries, emitting `ResolvedReference` findings at `Tier::Proven`.\n*   ✅ Content-hashed finding IDs so `impact_explain` can round-trip by ID across runs.\n*   ✅ `--context` bridge to `cargo-context` (forward-flow shipped via `cargo-context --files-from -`; JSON-envelope `--impact-scope` not scheduled — see §7)\n*   ⏳ Framework adapters: `axum`, `clap` (reference implementations); documented adapter trait for third parties\n*   ⏳ `cargo impact log-miss` for ground-truth collection\n*   ⏳ Token budgeting on markdown output\n*   ⏳ Configuration file (`cargo-impact.toml`) + `.impactignore`\n\n**Success metric:** A Claude Code / Cursor session can complete a non-trivial Rust refactor using only MCP tool calls — no shell output parsing, no manual context assembly.\n\n### v0.4 — \"Production-grade\"\n**Goal:** A Rust developer can drop `cargo-impact` into their CI pipeline, have it gate PRs, and trust the numbers. Today the tool works on a laptop; v0.4 is when it works in CI.\n\nWhat \"production-grade\" actually means, broken into concrete cuts.\n\n#### v0.4 core — must ship\n\nThe minimum set that makes the CI-gate story real. If v0.4 ships with only these, it's still a meaningful release.\n\n*   **`--format sarif`** — SARIF v2.1.0 output that GitHub code scanning, GitLab security, and every other security-scanner UI already knows how to render. Makes cargo-impact findings appear inline on PR diffs without any custom glue.\n*   **GitHub Actions composite action** — `uses: asmuelle/cargo-impact-action@v1` with sane defaults (runs on PR, uploads SARIF, comments the markdown report). Target: a first-time user can gate their repo with ≤10 lines of YAML.\n*   **PR-comment output mode** — `--format=pr-comment` renders the markdown optimized for GitHub PR comments (collapsed `\u003cdetails\u003e` per severity, severity-badge headers, `#\u003cN\u003e` cross-links to the SARIF upload). Complements the SARIF path for teams that don't run code scanning.\n*   **Deterministic output** — strip timestamps, pin sort orders, fix format-version strings, make two runs against the same diff byte-identical. Required for any CI that diffs output across runs or caches by content hash.\n*   **Benchmark suite + SLO regression gates** — `cargo bench`-based timing against a fixture matrix. Committed baselines in `benches/baseline.json`; CI workflow `.github/workflows/bench.yml` runs `scripts/bench-gate.sh` on every PR and fails if any bench's p50 exceeds baseline × 2.5 (threshold absorbs GH-runner variance, catches genuine 2x+ regressions). Re-baseline with `BENCH_GATE_MODE=update`.\n\n**v0.4 core success metric:** A maintainer of a real open-source Rust project (not us) can add cargo-impact to their CI in under 15 minutes following the README, and a PR that breaks a trait contract surfaces as a blocking annotation on their code-scanning UI.\n\n#### v0.4 stretch — ship if the core is solid\n\nHigher-value precision improvements that depend on the core being landed first. Any of these would be individually a meaningful release; we ship whichever are ready.\n\n*   ✅ **Macro expansion via `cargo expand`** — _shipped._ `--macro-expand` shells to `cargo expand --lib` (falling back to binary targets for bin-only crates), parses the output as one merged `syn::File`, and emits two classes of finding that syn-only analysis misses: (a) `TraitImpl` findings for impls synthesized by derive / attribute macros (serde, clap, thiserror, tokio); (b) `TestReference` findings for `#[test]` / `#[tokio::test]` / `#[rstest]` fns whose post-expansion body names a changed symbol — catches the `sqlx::query!(\"SELECT * FROM users\")` case where the raw source has only a literal string but expansion names the referenced struct. Findings use the `\u003cexpanded\u003e` sentinel path; a second dedup pass drops expanded test-refs whose test name is already covered by a raw-source `TestReference`. Full source-map back to the unexpanded file remains deferred (expansion loses line anchors). Graceful no-op when `cargo-expand` is absent from PATH.\n*   ✅ **Per-reference severity refinement** — _shipped._ Each `ResolvedReference` is classified by its enclosing container: test fn → `Low` (test-only), impl block → `High` (impl breakage propagates), caller → `Medium` (default). Syn-based classifier in `src/ref_context.rs`; RA still resolves the reference, we refine the severity.\n*   ✅ **syn/RA finding dedup and tier upgrade** — _shipped._ When a syn analyzer flags a site `Likely` and RA confirms it at `Proven`, the syn finding is dropped so the report doesn't double-count. See `src/dedup.rs`.\n*   ✅ **`--feature-powerset` (CI mode)** — _shipped (depth-1 scope)._ Runs the analyzer under baseline, `--no-default-features`, and `--all-features` in sequence, merging findings by content-hashed ID. Findings visible only under a non-baseline set are annotated in evidence with the feature set that revealed them. Full O(2^N) powerset across individual features is out of scope; the depth-1 view catches the common std/no_std and sync/async feature-gated blast radius without combinatorial blow-up.\n*   ✅ **Streaming progress over MCP** — _shipped._ The MCP `impact_analyze` tool emits `notifications/message` events (`level: \"info\"`, `logger: \"cargo-impact\"`, `data: { stage, current, total, detail? }`) at each analyzer stage boundary, ending with a `stage: \"done\"` event before the final `result`. Clients that ignore unknown notifications see no change; clients that render log messages get live feedback during the `--rust-analyzer` / `--semver-checks` long tail. Underlying API: `analyze_with_progress()` in the library.\n*   ✅ **More framework adapters** — _shipped: `actix-web` + `rocket`._ Method-chain visitors (`.route` / `.service` / `.scope` / `.mount`) plus a shared HTTP-verb attribute-macro pass with framework disambiguation via use-statement scan. `tauri` / `dioxus` / `leptos` remain on-demand.\n\n#### v0.4 explicit non-goals\n\nCalled out here so nobody expects them landing in this milestone — each is a real user-facing request, each is deliberately scoped out.\n\n*   **`no_std` / `wasm32` cross-target support** — genuinely useful, but requires target-triple-aware cfg evaluation and a second analysis pass. Moved to v0.5.\n*   **Polyrepo / cross-workspace** — path dependencies and git dependencies spanning repos. Design work needed before scoping; Beyond-v0.4.\n*   **`git bisect` driver using impact data** — neat, but narrow enough user base to wait for demand.\n*   **`cargo-difftests` coverage integration** — external tool still pre-1.0; fall back to call-graph-only test selection until its shape stabilizes.\n*   **IDE integration (VS Code / Zed)** — users can already get structured output via the MCP server. A dedicated editor extension is Beyond-v0.4.\n*   **Shared `target/ai-tools-cache/` with cargo-context** — depends on maintainer alignment on cache format; treat as v0.5+ joint work.\n\n### Beyond v0.4\nUnscheduled, prioritized by demand rather than by us:\n\n*   IDE integration (VS Code extension, Zed LSP plugin)\n*   `git bisect` driver using impact data to narrow the search\n*   Historical blast-radius mining (find under-tested hot spots across a year of history)\n*   Cross-workspace impact for polyrepo setups with path dependencies\n*   `--impact-scope` JSON-envelope consumer on the cargo-context side (pending cargo-context maintainer scheduling; schema proposal on record at [cargo-context#5](https://github.com/asmuelle/cargo-context/issues/5#issuecomment-4304409079))\n\n### What could kill each milestone\nHonest risk log, not hand-wave:\n\n| Milestone | Biggest risk | Mitigation |\n| :--- | :--- | :--- |\n| v0.1 | `ra_ap_*` API churn between rust-analyzer releases | Pin RA version per release; fall back to LSP protocol if library API breaks |\n| v0.2 | Macro expansion is too slow or too incomplete on real workspaces | Downgrade confidence on unexpanded macros rather than fail; document known-bad proc macros |\n| v0.3 | MCP ecosystem fragments before stabilizing | Ship stdio + Streamable HTTP; keep CLI first-class so tool isn't MCP-dependent |\n| v0.4 | SARIF shape evolves after we ship; GitHub Actions billing or permissions model changes for the composite action | Target SARIF v2.1.0 (well-established, used by every major scanner); keep `--format=json` as the stable ground truth so SARIF is a downstream renderer; composite action is a thin wrapper with no business logic. `cargo-difftests` specifically — fall back to call-graph-only test selection (less precise but still useful) if that tool doesn't stabilize. |\n\n---\n\n## License\n\nDual-licensed under either of\n\n* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or \u003chttp://www.apache.org/licenses/LICENSE-2.0\u003e)\n* MIT License ([LICENSE-MIT](LICENSE-MIT) or \u003chttp://opensource.org/licenses/MIT\u003e)\n\nat your option. Per Rust ecosystem convention.\n\n### Contribution\n\nUnless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasmuelle%2Fcargo-impact","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fasmuelle%2Fcargo-impact","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasmuelle%2Fcargo-impact/lists"}