A chained command is split into independently cached sub-tasks, but `output` stays task-level — a hit replays a stale output over a fresh one
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 48/100
- Tipo de issue
- Error
- Claridad
- Bastante claro
- Estado de actividad
- Tranquilo
- Stack tecnológico
- rust
- Área
- build-system, tooling
Línea de trabajo
Reproduce el problema con la tarea vite.config.ts y vp run gen, cambiando únicamente src-a.txt entre ejecuciones. Empieza rastreando el comportamiento de archivado y restauración de la caché para subtareas encadenadas y output compartido; se considera terminado cuando un acierto parcial no puede restaurar las salidas de una tarea hermana, o cuando se rechaza almacenar la tarea en la caché con una advertencia clara.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
When a task's command chains several commands (a && b, or an array), Vite+ caches each sub-command independently, but the task declares a single, task-level output. Every sub-task therefore claims every output file.
On a partially-cached run this is unsound: a sub-task that hits restores its archived copy of the shared outputs over the file a sub-task that missed has just regenerated. The task exits 0 and the artifact on disk is stale.
Ordering makes it visible: with 3 hits and 1 miss on the same task, the missing sub-task in position 2 of 4 leaves a stale output, while the same sub-task in position 4 of 4 leaves the correct one. Clearing the cache (no replay at all) is always correct.
This is not an input-declaration problem. Fixing input so the miss fires correctly does not fix it: the sub-task runs, writes the right file, and a later hit overwrites it.
Reproduction
mkdir vp-chained-output && cd vp-chained-output
npm init -y
npm i -D vite-plus@0.2.7
mkdir -p out
vite.config.ts:
import { defineConfig } from 'vite-plus';
export default defineConfig({
run: {
tasks: {
gen: {
command: [
'node -e "require(\'fs\').writeFileSync(\'out/a.txt\', require(\'fs\').readFileSync(\'src-a.txt\',\'utf8\'))"',
'node -e "require(\'fs\').writeFileSync(\'out/b.txt\', require(\'fs\').readFileSync(\'src-b.txt\',\'utf8\'))"',
],
output: ['out/**'],
},
},
},
});
Automatic tracking matters here: it is what gives each sub-task a different input set, so one can hit
while another misses. With a single explicit input list, every sub-task invalidates together and the bug
stays hidden.
echo v1 > src-a.txt && echo v1 > src-b.txt
npx vp run gen # miss, out/a.txt = v1, out/b.txt = v1
npx vp run gen # hit
echo v2 > src-a.txt # only the first sub-command's input changes
npx vp run gen # -> "cache miss: 'src-a.txt' modified" for sub-task 1
# -> "cache hit" for sub-task 2
cat out/a.txt # expected v2 - observed v1, exit code 0
Observed exactly as above on vite-plus 0.2.7.
Expected
Each sub-task restores only the outputs it produced, or the task is treated as a single cache unit.
Actual
The archive of a hitting sub-task contains files written by its siblings and replays them, silently reverting fresher output. Exit code is 0.
Why it matters in practice
In our monorepo the pattern is common: a generator followed by a fixer, e.g.
kubb generate ./swagger.yml -c=kubb.config.mock.js && npm run fix-generated-mock-imports
Both write under src/gen/mocks/**, which is the task's only declared output. A partial hit yields a half-regenerated SDK with no signal. We found 14 tasks in this shape and had to disable caching on all of them.
Splitting them into one task per command — the obvious workaround, and the one we could apply to a Panda ship chain — is not possible when the second command fixes the output of the first: the fixer has the same files as input and output, so the tracker declines to cache it (Not cached: read and wrote …).
Suggested directions
- track outputs per sub-task rather than per task, so a sub-task only restores what it wrote; or
- treat a task with a chained command and more than one output as a single cache unit; or
- at minimum, refuse to cache (or warn) when a task chains commands and declares outputs that several sub-tasks can write — a loud refusal is far better than a silent stale artifact.
Environment
- vite-plus
0.2.7 - macOS
darwin 25.5.0, arm64; Node22.22.0, pnpm9.15.0
Related
- voidzero-dev/vite-task#587 — the tracker not observing reads made by a Go binary. Different mechanism, same consequence: a cache hit that hides a wrong result.
- Lenguaje dominante
- Rust
- Estrellas
- 466
- Forks
- 42
- Merge medio
- 1 d 20 h
- PR fusionados (30 d)
- 21
Guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de voidzero-dev/vite-task
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 35/100
voidzero-dev/vite-task#738 · 1 comentario ·
-
Dificultad 3/5 1-2 días Aptitud para principiantes 78/100
voidzero-dev/vite-task#719 ·
-
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
voidzero-dev/vite-task#717 ·
-
Dificultad 3/5 1-2 días Aptitud para principiantes 68/100
voidzero-dev/vite-task#702 ·
-
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
voidzero-dev/vite-task#700 · 2 comentarios ·
Todos los issues de voidzero-dev/vite-task
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
-
state:needs triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
zed-industries/zed#64680 · 2 comentarios ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
RustPython/RustPython#8802 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
TheLarkInn/aipm#2390 ·