> ## Documentation Index
> Fetch the complete documentation index at: https://site.aspect.build/llms.txt
> Use this file to discover all available pages before exploring further.

# aspect cache diff

> Determine which test targets are affected by the current source tree by mining a single remote-cache probe with aspect cache diff — runs no tests.

`aspect cache diff` lists the **test targets affected by the current working tree** by diffing it against your remote cache. It runs no tests. A single Bazel invocation with `--experimental_remote_require_cached` probes the cache: every cache hit means "unaffected", and every miss is attributed to the dependent tests.

Strictly a **remote-cache operation** — no remote build execution (RBE) is required.

```shell theme={null}
aspect cache diff                       # affected tests under //...
aspect cache diff //services/...        # scope to a sub-tree
aspect cache diff --format=json         # scriptable output
aspect cache diff --exec="bazel test"   # pipe affected labels into a command
```

## When to use it

* **Debug cache misses.** Find the exact set of cache-missed actions that explain why a CI run rebuilt or re-ran more than expected.
* **Validate CI cache coverage.** Run it against a clean checkout to confirm your CI is uploading the baseline you expect — a populated cache should report `Affected 0 of N`.
* **Pre-flight a change.** Before pushing, list the tests that are actually affected by your edits and pipe them into `bazel test` to run only what matters.
* **Drive selective CI.** Use the `lines` or `json` output to gate downstream jobs on the affected target set.

## How it works

1. Enumerates the test targets in scope with `tests(<patterns>)`.
2. Runs one Bazel invocation with `--experimental_remote_require_cached`. Each action's up-to-date check resolves against the remote cache; on a miss, execution is denied so the probe runs nothing.
3. Reads the gRPC log of `GetActionResult` calls. A miss anywhere in a test's transitive closure trips at that action — the *frontier*.
4. Attributes the affected tests from the frontier (overreport) or only from missing test actions (precise).

Affected labels stream to stdout; progress, per-target reasons, and the summary go to stderr — so you can pipe stdout cleanly.

## Prerequisites

* A writable `--remote_cache` configured in `.bazelrc` (or via `--bazel-flag`). The task fails fast if no remote cache is configured.
* A **populated baseline**: a cache hit is what makes a target "unaffected", so the mainline's action results must already be uploaded. Normally this happens as a byproduct of regular CI:

  ```shell theme={null}
  bazel test //... --remote_upload_local_results
  ```

  Seed on a clean checkout (or a fresh `--output_base`) — on a warm output base Bazel finds everything locally cached, executes nothing, and uploads nothing. CI checkouts are cold, so this is automatic there.
* The probe and the baseline must resolve the **same Bazel flags**, so they compute the same action digests. The probe reuses the same rc/config as `aspect build` / `aspect test`, so they line up by default. A flag that changes an action's environment (for example `--action_env=FOO`) must be present on both sides or neither.

## Modes

Pick a mode with `--mode`:

| Mode                   | What it does                                                                                                                           | Cost                                                                                          |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `overreport` (default) | Reverse-deps from each cache-missed target to its dependent tests.                                                                     | Runs nothing. May flag a test even when its dependency would rebuild to an identical output.  |
| `precise`              | First runs `bazel build` to build and upload the missing non-test actions, then flags a test only when its **own** test action misses. | Costs the build of the missing non-test actions. Narrows the result to genuine input changes. |

**Execution guarantee:** *no test ever runs* in either mode. `overreport` executes nothing; `precise` executes only the missing non-test actions (it builds with `bazel build`, which cannot execute `TestRunner`). The lone exception is actions tagged `no-remote`, `local`, `no-remote-exec`, or `no-cache` — those bypass the remote path entirely, so they run locally and read as affected.

## Output

By default, affected labels stream to stdout, one per line:

```shell theme={null}
$ aspect cache diff
//backend/api:integration_test
//backend/api:unit_test
//libs/auth:token_test
```

Pipe directly into Bazel:

```shell theme={null}
aspect cache diff | xargs bazel test
```

Or hand the labels to any command with `--exec`:

```shell theme={null}
aspect cache diff --exec="bazel test"
```

For scripting, switch to JSON:

```shell theme={null}
$ aspect cache diff --format=json
{
  "mode": "overreport",
  "total_tests": 124,
  "affected_count": 2,
  "affected": [
    {
      "label": "//backend/api:integration_test",
      "caused_by": [
        { "target": "//libs/auth:token", "mnemonic": "GoCompile" }
      ]
    },
    {
      "label": "//libs/auth:token_test",
      "caused_by": [
        { "target": "//libs/auth:token_test", "mnemonic": "TestRunner" }
      ]
    }
  ]
}
```

Progress, per-target reasons (including the cache-missed target that each test is "caused by"), and the final `Affected X of N test target(s).` summary go to stderr.

## Configuration flags

| Flag                   | Default      | Description                                                                                                                                                     |
| ---------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--mode`               | `overreport` | `overreport` or `precise`. See [Modes](#modes).                                                                                                                 |
| `--format`             | `lines`      | `lines` (one label per line) or `json` (single document on stdout).                                                                                             |
| `--exec`               | (empty)      | Run the given command with the affected labels appended as arguments (for example `--exec="bazel test"`). No-op when nothing is affected. Overrides `--format`. |
| `--bazel-flag`         | —            | Additional Bazel flags forwarded to the probe and the precise build (for example `--bazel-flag=--config=ci`). Repeatable.                                       |
| `--bazel-startup-flag` | —            | Additional Bazel startup flags. Repeatable. Note that changing startup flags restarts the Bazel server.                                                         |
| *positional*           | `//...`      | Target patterns scoping the affected set (intersected with `tests(<patterns>)`).                                                                                |

Run `aspect cache diff --help` for the complete list of flags.

## Examples

**Scope to a sub-tree and run only the affected tests:**

```shell theme={null}
aspect cache diff //services/... | xargs bazel test
```

**Use a CI config so the probe matches your CI baseline's flags:**

```shell theme={null}
aspect cache diff --bazel-flag=--config=ci //...
```

**Narrow `overreport` noise to genuine input changes:**

```shell theme={null}
aspect cache diff --mode=precise //...
```

**Drive a downstream job from JSON:**

```shell theme={null}
aspect cache diff --format=json > affected.json
jq -r '.affected[].label' affected.json | xargs bazel test
```

## Soundness and limits

The result is bounded by your baseline's cache coverage:

* A hit means the target is **unaffected**.
* A miss means the target is **either changed or never-uploaded/evicted**.

Against a fully-populated baseline this is exact. Against a sparse cache, `overreport` over-reports and `precise` narrows the result to genuine input changes.

The probe runs with `--nokeep_state_after_build --nouse_action_cache`, which forces a fresh remote `GetActionResult` per action — so the command runs on the default output base with no separate or wiped base.

## Related

* [`aspect build` & `aspect test`](/docs/cli/tasks/build_test) — the same task lifecycle that seeds the cache baseline.
* [Aspect Workflows](/docs/aspect-workflows/overview) — Aspect Workflows runners auto-configure the remote cache that `aspect cache diff` reads from.
