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_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:
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:
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.
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 configshowed two development exec configurations with different hashesbut the same output-directory name. Their only material difference was that the
test-side configuration retained Bazel's
TestConfiguration$TestOptionsfragment while the web-side configuration did not.
The web dependency transition reported by
cqueryincluded: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_configurationconsistently to both commands convergedthe 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:
running and 11,730 action-cache hits.
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_resultsor--test_outputinvocation) changes the sharedconfiguration 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:
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
TestTrimmingTransitionandConfigFeatureFlagTaggedTrimmingTransition.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.