Alternating Valdi test and web targets recompiles shared exec tools
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:
bazel build //:example_app_npm
bazel test //modules/example_app:test
bazel build //:example_app_npmRepeat 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:
bazel build //:example_app_npm --notrim_test_configuration
bazel test //modules/example_app:test --notrim_test_configurationMeasured 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:
build:stable_exec --notrim_test_configurationThis 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.
Source: Snapchat/Valdi