aspect cache diff finds that set by asking the remote cache rather than analyzing the source: it lists the test targets affected by the current working tree and runs no tests. Which tests are affected? Ask the cache, run nothing. explains how it works and how it compares with other approaches.
Two Bazel invocations on the same warm output base probe the cache with --experimental_remote_require_cached: every cache hit means “unaffected”, and every miss is attributed to the dependent tests.
It’s a remote-cache operation; it needs no remote execution.
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 uploads the baseline you expect. A populated cache reports
Affected 0 of N. - Pre-flight a change. Before pushing, list the tests affected by your edits and pipe them into
bazel testto run only what matters. - Drive selective CI. Use the
linesorjsonoutput to gate downstream jobs on the affected target set.
How it works
- Enumerates the test targets in scope with
tests(<patterns>). - 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. - Reads the gRPC log of
GetActionResultcalls. A miss anywhere in a test’s transitive closure trips at that action, the frontier. - Attributes the affected tests from the frontier (overreport) or only from missing test actions (precise).
Prerequisites
-
A writable
--remote_cache, in.bazelrcor on the command line. The task stops early if none 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:
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. -
Bazel 7 or later. Test selection follows Bazel’s test patterns and filters. Wildcards such as
//...excludemanual-tagged tests; name amanualtest explicitly to include it. Pass negative patterns after--. -
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:
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: they bypass the remote path entirely, so they run locally and read as affected.
Output
By default, affected labels stream to stdout, one per line:--exec:
Affected X of N test target(s). summary go to stderr.
Configuration flags
Run
aspect cache diff --help for the complete list of flags.
Examples
Scope to a sub-tree and run only the affected tests:overreport noise to genuine input changes:
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.
overreport over-reports and precise narrows the result to genuine input changes.
The probe runs in two passes on one warm output base. The first flips a salt in the action’s exec properties, which invalidates the ACTION_EXECUTION nodes without discarding the analysis; the second re-checks them with --nouse_action_cache, forcing a fresh remote GetActionResult per action. Keeping the analysis is the point — it needs no separate or wiped output base, and it replaces the old --nokeep_state_after_build, which re-analyzed on every probe.
Related
aspect build&aspect test: the same task lifecycle that seeds the cache baseline.- Driving the CLI from scripts and AI agents: using
aspect describeand--output=jsonto script or automate the CLI. - Aspect Workflows: Aspect Workflows runners configure the remote cache that
aspect cache diffreads from.

