#137·Valdi

Alternating Valdi test and web targets recompiles shared exec tools

Author: clholgatCreated Aug 4, 2026Updated Aug 4, 2026

Summary

Alternating between a generated Valdi module test target and a Valdi web/npm package target can recompile thousands of shared native tool actions even when the source tree has not changed.

This was reproduced in a generic external Valdi consumer workspace on macOS arm64 with Bazel 8.3.1. All commands used the same Bazel server, output base, and local execution policy, so this is separate from sandbox/output-directory permission problems.

Reproduction

Using a consumer module that builds both a generated Valdi test and a web/npm package:

bash
bazel build //:example_app_npm
bazel test //modules/example_app:test
bazel build //:example_app_npm

Repeat the sequence without changing source files.

Observed default transitions repeatedly took roughly 198–239 seconds and ran about 3,397–3,401 processes. Immediate repeats of the same target were warm.

Configuration evidence

bazel config showed two development exec configurations with different hashes but the same output-directory name. Their only material difference was that the test-side configuration retained Bazel's TestConfiguration$TestOptions fragment while the web-side configuration did not.

The web dependency transition reported by cquery included:

exec + (TestTrimmingTransition + ConfigFeatureFlagTaggedTrimmingTransition)

Because the two action configurations write beneath the same exec output path, switching target graphs appears to displace shared compiler/tool outputs. A disabled disk cache makes the effect especially visible, but a disk cache would only mask the configuration collision.

Validated workaround

Passing --notrim_test_configuration consistently to both commands converged the web and test graphs on one top-level configuration and one development exec configuration:

bash
bazel build //:example_app_npm --notrim_test_configuration
bazel test //modules/example_app:test --notrim_test_configuration

Measured results after convergence:

  • First web transition: 31.3 seconds, 48 processes, 6,384 action-cache hits.
  • Reverse transition to the test: 0.8 seconds, with the passing test reused.
  • Forced execution of 385 specs: 14.6 seconds, with only the test process running and 11,730 action-cache hits.
  • An unchanged full test-then-web verification subsequently reused the test in 0.469 seconds and the web package in 0.453 seconds, with no targets reconfigured and no compilation.

A clean analysis configured the same 41,284 targets with and without trimming, and server RSS did not increase in the no-trim run, so this workaround did not show a graph-size or memory regression in this workspace.

One caveat: with no trimming, changing test flags (for example a diagnostic --nocache_test_results or --test_output invocation) changes the shared configuration and can cause one repopulation build. Ordinary scripted commands remain warm when their flags are stable.

Proposed fix

Short term, provide a named configuration in generated/consumer Valdi projects and use it consistently in the standard web-package and test scripts:

bazel
build:stable_exec --notrim_test_configuration

This should be named rather than global so arbitrary direct Bazel commands keep the default behavior, and documentation should warn that diagnostic test flags can repopulate the shared configuration.

Longer term, please investigate the composition of the Valdi exec transition with TestTrimmingTransition and ConfigFeatureFlagTaggedTrimmingTransition. The ideal rules-level fix would either normalize irrelevant test options before entering the shared exec tool graph or ensure configurations with different action keys cannot collide in the same exec output path. That would preserve test trimming without requiring consumer projects to opt out globally.

Related but distinct

This is different from #15, which recommends a disk cache for sharing artifacts between separate projects. The reproduction here occurs while alternating two targets inside one unchanged workspace and one Bazel server.