Skip to content
vxvx
GitHubBlueskydev.toRSS
Releases

Every cache key known before a task runs

Two workspace rules that let vx know every key before anything runs, servers that stay off when only cache hits need them, and a live OpenTelemetry export.

vx 0.0.589: Every cache key known before a task runs

vx 0.0.521 to 0.0.589 shipped on 2026-10-07. The theme is fewer surprises: the graph refuses configs that would delete files or slow every run, a fully cached run boots nothing it does not need, and telemetry tells you why a task rebuilt.

In this release

A task whose input globs could match another task’s outputs used to get a preliminary key: vx could not probe, prefetch or restore it ahead of the schedule. The new rules.upfrontKeys refuses such a task at load and names the ! entry that fixes it. It is on by default. The producer’s key already reaches the reader through dependsOn, so excluding its outputs loses nothing. rules: { upfrontKeys: false } in vx.workspace.ts keeps the old waiting path.

Terminal window
vx: app#test reads "**/*" in cache.inputs.files, which matches app#build's
output "dist/**": a task's key must not read another task's outputs (the
dependency's key already cascades through dependsOn). Exclude it: add
"!dist/**" to app#test's cache.inputs.files, or set rules: { upfrontKeys:
false } in vx.workspace.ts to let it wait for its producer.

PR #2846. Docs: Workspace config.

Two tasks whose outputs overlapped were allowed when a dependsOn edge ordered them. That was correct, but every run of the dependant paid for a stamp, a diff and a clean. rules.exclusiveOutputs, on by default, refuses the ordered pair too. Give each task its own output path, or set rules: { exclusiveOutputs: false } to keep the additive path. Output globs that overlap without sharing a prefix, such as dist/**/*.js beside dist/sth/**, are now caught as well.

Terminal window
vx: app#types and app#build both declare the output "dist/**" in
cache.outputs.files: vx cleans a task's declared outputs before it runs
and before a cache-hit restore, so whichever of these runs second DELETES
the other's output. Give each task its own output path, or set rules: {
exclusiveOutputs: false } in vx.workspace.ts to let a dependant add to its
upstream's outputs.

PRs #2845, #2841. Deep dive: Strict output ownership.

vx removes a task’s outputs before it runs. A formatter that declared src/** as both inputs and outputs had every committed file under src deleted. The graph now refuses it, whatever the rules, and says what to do instead.

Terminal window
vx: app#fmt: every file "src/**" in cache.inputs.files selects is also its
own output: vx removes a task's outputs before it runs, so the task would
delete its own sources. A task that rewrites files in place (a formatter)
declares no outputs.

PR #2847.

A persistent task pulled in as a dependency booted on every run, and its dependants waited for it to be ready even when they were cache hits. Now vx holds such a server dormant. It starts only if a dependant misses, and otherwise never spawns. A fully cached vx run e2e#test over web#dev went from 2.2 s to under 190 ms. Dependants in the server’s own project still wait for it.

Terminal window
vx run e2e#test
Cached vx run e2e#test over web#dev
before2.2 s
afterunder 190 ms

PR #2851. Deep dive: Dev servers in the graph.

@vzn/vx-otel now sends each task as it ends, batched once a second, so a dashboard follows a CI run while it runs. live: false sends it all at the end. Traces, metrics and logs share one resource key. Task spans gain the command, queue wait, input file count, artifact size, save and fetch time, and for a miss, what its key changed since the last saved entry. On CI, every signal links back to the run and the pull request.

Terminal window
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 vx run build --all

PRs #2786, #2787, #2792, #2794. Docs: OpenTelemetry.

A migrated repo used to end with a note asking you to declare the lockfile plugin yourself. @vzn/vx-migrate now writes the plugins into vx.workspace.ts and installs them at its own version: the lockfile’s @vzn/vx-lockfile plugin, scheduleHistoryPlugin(), and github() from @vzn/vx-ci when .github/workflows exists. Written files no longer carry a “Generated by” header. vx also stops printing cache upkeep notices on upgrade.

Terminal window
npx @vzn/vx-migrate

PR #2777. Docs: One command.

Several cuts to the warm path. Task globs match through one RegExp per side instead of a dozen Bun.Glob calls per file. @vzn/vx-lockfile, @vzn/vx-ci and @vzn/vx-mcp load their internals only when used, and @vzn/vx-otel imports in 1.4 ms instead of 9.6 ms. A repo with no remote no longer spawns two git commands on the run path. Together they make the all-cached run of the vx repo itself 25% faster.

Terminal window
vx run build --all
@vzn/vx-otel import
before9.6 ms
after1.4 ms

PRs #2785, #2788, #2842.

vx watch lib#build watched every project; it now watches lib and the projects other pkg#task arguments name. vx watch --affected re-runs tasks for edits made after it started, not only those in the starting diff. An edit inside a nested project no longer triggers a cycle on the root project.

Terminal window
vx watch lib#build

PRs #2828, #2827, #2832.

Args after -- now reach a command that ends in a heredoc or a trailing newline, where before they never arrived or ran as a command of their own (--watch: not found). The $ line in the output shows the command with the args, as it ran.

Terminal window
vx run test -- --watch

PRs #2790, #2802, #2831.

Several runs could store or replay wrong bytes under a green result. A file added under an input glob while the command ran is now caught and the save withheld. Committed files under a node_modules directory, such as test fixtures, are part of the key again. The clean before a run no longer follows an output directory linked to another directory in the project. A dependant that deletes its upstream’s files no longer gets them restored on every hit.

Terminal window
vx run build --all

PRs #2854, #2793, #2795, #2822, #2823.

  • CACHE_VERSION is now vx-cache-v40: every cached task misses once after the upgrade (#2825).

See Upgrading.

Terminal window
npm install -D @vzn/vx@latest

A standalone binary updates itself with vx upgrade.

Turborepo, Nx and other product names are trademarks of their owners. vx is not affiliated with or endorsed by them.