vx never guesses what a task reads. You list its inputs. Here is why:
the worst thing a build cache can do is restore the wrong file. This
task copies notes.md, but its inputs name only src/**:
notes: {
exec: { command: 'mkdir -p out && cp notes.md out/notes.md' },
inputs: { files: ['src/**'] },
cache: { outputs: { files: ['out/**'] } },
}
$ vx run packages/a#notes
local 1↑
$ cat packages/a/out/notes.md
version 1
$ echo "version 2" > packages/a/notes.md
$ vx run packages/a#notes
local 1✓
$ cat packages/a/out/notes.md
version 1
A green run, a cache hit, and the wrong file. We call that a stale
hit. It came from one missing input.
Why not trace the inputs?
Some tools record the files a task opens. That list can't be the key:
- It comes too late. The key is needed before the task runs. A trace exists only after.
-
It sees one path. A build that reads
config/prod.jsonin production andconfig/dev.jsonotherwise has two traces. -
Wide defaults rebuild too much. With
**as the default, a README edit rebuilds every task in the package.
Only the task's author knows what it depends on.
The two mistakes are not equal
| Mistake | What happens | Cost |
|---|---|---|
| Extra file listed | the task runs again | an extra run: visible, cheap |
| File missing from the list | the old output is restored | a stale hit: green, silent, wrong |
Tracing makes the second kind, because one run never sees every file a
task can read. Listing inputs lets you err toward the first.
For the second, the sandbox helps: give it reads no
wider than the inputs, and the notes.md read above fails the run and
names the path.
vx why explains any re-run.
Explicit everywhere
-
cacheis opt-in. No block, no cache, no default globs. -
dependsOnis explicit.^taskskips a package without that task. - Past the essential allowlist every task gets (
PATH,HOME,CI,NODE_OPTIONSand a few more), env reaches a task only throughexec.env. - A variable that only has to be present goes under
passThrough. One that changes the output also goes underinputs.env, which puts it in the key. - The sandbox reads nothing from
cache. What a task may touch and what changes its key are separate lists.
Demo · vx 0be0367 · 2026-10-23
Write one glob, and skip an afternoon of debugging a stale hit. More at
vznjs.github.io/vx.
Originally published on the vx blog. vx is open-source (MIT) task orchestration with a build cache: vznjs.github.io/vx · GitHub.
Written with AI assistance.
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.