@vzn/vx 0.0.639 → 0.0.641
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/docs/caching.md +158 -106
- package/docs/cli.md +80 -45
- package/docs/execution.md +11 -10
- package/docs/features.md +9 -8
- package/docs/patterns.md +1 -1
- package/docs/schema.md +252 -190
- package/docs/security.md +5 -4
- package/launcher.sh +9 -2
- package/package.json +5 -5
- package/schemas/show.json +14 -14
- package/skills/vx/SKILL.md +4 -3
- package/src/bin.ts +8 -6
- package/src/cache/archive.ts +248 -41
- package/src/cache/cache.ts +46 -19
- package/src/cache/file-hashes.ts +7 -12
- package/src/cache/fs-clock.ts +62 -0
- package/src/cache/git-inputs.ts +2 -2
- package/src/cache/index.ts +6 -0
- package/src/cache/inputs.ts +228 -111
- package/src/cache/key-fold.ts +23 -3
- package/src/cache/layer.ts +15 -8
- package/src/cache/layered-cache.ts +8 -7
- package/src/cache/linked-inputs.ts +87 -0
- package/src/cache/scoped-cache.ts +8 -2
- package/src/cli/foreign-flags.ts +1 -1
- package/src/cli/help.ts +7 -1
- package/src/cli/init.ts +23 -11
- package/src/cli/last.ts +24 -1
- package/src/cli/plugin-templates.ts +2 -2
- package/src/cli/select.ts +16 -6
- package/src/cli/show.ts +9 -6
- package/src/cli/upgrade.ts +132 -21
- package/src/cli/watch-filter.ts +23 -12
- package/src/cli/watch-fs.ts +2 -1
- package/src/cli/watch-judge.ts +49 -5
- package/src/cli/watch-set.ts +18 -8
- package/src/cli/watch.ts +74 -2
- package/src/cli/why.ts +5 -3
- package/src/config.ts +19 -15
- package/src/exec/executor.ts +4 -4
- package/src/graph/dependency-spec.ts +2 -2
- package/src/graph/folds.ts +236 -0
- package/src/graph/index.ts +8 -1
- package/src/graph/scheduler.ts +41 -15
- package/src/graph/task-graph.ts +135 -73
- package/src/handoff.ts +5 -0
- package/src/index.ts +2 -2
- package/src/orchestrator/affected-tasks.ts +20 -19
- package/src/orchestrator/download-policy.ts +16 -22
- package/src/orchestrator/excluded-keys.ts +4 -5
- package/src/orchestrator/execute-task.ts +29 -25
- package/src/orchestrator/hit-restore.ts +101 -27
- package/src/orchestrator/index.ts +1 -0
- package/src/orchestrator/keyed-projects.ts +12 -51
- package/src/orchestrator/local-shortcircuit.ts +9 -1
- package/src/orchestrator/persistent-task.ts +3 -3
- package/src/orchestrator/placement.ts +2 -2
- package/src/orchestrator/plan.ts +1 -1
- package/src/orchestrator/plugin-host.ts +66 -27
- package/src/orchestrator/plugin.ts +12 -2
- package/src/orchestrator/prepare.ts +12 -5
- package/src/orchestrator/projects.ts +7 -10
- package/src/orchestrator/run-report.ts +145 -46
- package/src/orchestrator/run-steps.ts +57 -0
- package/src/orchestrator/run.ts +76 -44
- package/src/orchestrator/sandbox-fix.ts +1 -1
- package/src/orchestrator/sandbox-request.ts +2 -2
- package/src/orchestrator/stable-keys.ts +59 -26
- package/src/orchestrator/steps.ts +26 -6
- package/src/orchestrator/task-hash.ts +31 -9
- package/src/orchestrator/telemetry-host.ts +3 -3
- package/src/orchestrator/telemetry.ts +9 -4
- package/src/orchestrator/upstream.ts +4 -181
- package/src/report/framed-output.ts +1 -1
- package/src/report/logger.ts +5 -2
- package/src/report/status-line.ts +7 -3
- package/src/report/summary.ts +6 -4
- package/src/settings.ts +11 -17
- package/src/util/index.ts +3 -0
- package/src/util/paths.ts +39 -5
- package/src/workspace/affected.ts +81 -2
- package/src/workspace/config-eval.ts +2 -2
- package/src/workspace/config-imports.ts +1 -1
- package/src/workspace/config-schema.ts +142 -143
- package/src/workspace/lockfile.ts +1 -1
- package/src/workspace/migration.ts +19 -3
- package/src/workspace/project-loader.ts +5 -4
package/docs/cli.md
CHANGED
|
@@ -37,7 +37,7 @@ vx show [PROJECT[#TASK] | TASK] [--filter <pattern>] [--affected[=<ref>]] [--for
|
|
|
37
37
|
vx info [--format pretty|json] [--cache-dir <path>]
|
|
38
38
|
vx why (TASK | PKG#TASK) [--run RUNID] [--format pretty|json] [--cache-dir <path>]
|
|
39
39
|
vx last [RUNID] [--list[=N]] [--failed] [--log <task>] [--stats [<task>] [--branch <name>]] [--format pretty|json] [--cache-dir <path>]
|
|
40
|
-
vx upgrade [TAG]
|
|
40
|
+
vx upgrade [TAG] [-g|--global]
|
|
41
41
|
vx docs <query> [--limit N] [--format pretty|json]
|
|
42
42
|
vx completions bash|zsh|fish
|
|
43
43
|
|
|
@@ -301,13 +301,13 @@ change seeds tasks in the projects it touches:
|
|
|
301
301
|
repository is one its `workspaceFiles` may reach into (git reports it
|
|
302
302
|
as one path; a glob whose fixed prefix is above or inside it counts);
|
|
303
303
|
- every task of a project whose `vx.config.*`
|
|
304
|
-
changed, or that holds a changed path no
|
|
304
|
+
changed, or that holds a changed path no task of its declares
|
|
305
305
|
(vx cannot prove that path re-keys nothing), or that holds a changed
|
|
306
306
|
submodule or embedded repository (git reports it as one path, and no
|
|
307
307
|
task's globs can say whether they reach inside), or that a lockfile
|
|
308
308
|
claim or a config import names (a dependency an install added or
|
|
309
309
|
removed reaches here as the lockfile's change);
|
|
310
|
-
-
|
|
310
|
+
- a task with no `inputs` when a changed path lies in its project or the
|
|
311
311
|
project is reached whole; a root file another task of its declares
|
|
312
312
|
(`workspaceFiles`) is no change there. A group seeds nothing.
|
|
313
313
|
|
|
@@ -354,6 +354,14 @@ change to it, and every project under it is selected, the project that
|
|
|
354
354
|
holds it included. A repository's own request to hide submodules from a diff
|
|
355
355
|
(`diff.ignoreSubmodules`, `submodule.<name>.ignore`) does not apply:
|
|
356
356
|
the key sees the change whatever git is told to show.
|
|
357
|
+
A symlinked input keys what it points to, so a link whose target
|
|
358
|
+
changed (a file the diff names, or one under a linked directory) is a
|
|
359
|
+
changed path of its project, and a link to a target git cannot diff
|
|
360
|
+
(outside the workspace, or git-ignored) is one on every run.
|
|
361
|
+
A gitignored file a task's inputs name outright (`.env.local`,
|
|
362
|
+
`dist/**`) never selects the task on its own, as in Nx: `--affected`
|
|
363
|
+
reflects only what git sees. The key still hashes it, so a run of the
|
|
364
|
+
task misses when it changed.
|
|
357
365
|
`.vx/projects.lock.json` is filtered out of the changed set — a `vx lock`
|
|
358
366
|
re-write never marks every project affected.
|
|
359
367
|
|
|
@@ -766,22 +774,26 @@ other persistent task`), and a non-zero exit makes the run exit 1
|
|
|
766
774
|
local history knows the task, its row also shows its usual duration
|
|
767
775
|
(its executed p50) in the cache column, fixed while the elapsed time
|
|
768
776
|
counts up toward it (`3.33s running ~5.2s solid-js#types`).
|
|
769
|
-
4. **vx's own steps** — what vx does around the tasks
|
|
777
|
+
4. **vx's own steps** — what vx does around the tasks that the run
|
|
778
|
+
waits on, before its first task starts or before it ends, as rows of
|
|
770
779
|
their own below the worker rows: `vx:load` (configs and graph from
|
|
771
780
|
the lock), `vx:run-lock`, `<plugin>:setup`, `vx:keys` (keys and local
|
|
772
|
-
probes),
|
|
773
|
-
`vx:
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
781
|
+
probes), `<plugin>:schedule`, then after the graph `vx:uploads`,
|
|
782
|
+
`vx:history`, `<plugin>:telemetry`, `<plugin>:teardown`,
|
|
783
|
+
`vx:snapshots`, `vx:retention`, `vx:close` and `vx:sandbox`. A step
|
|
784
|
+
shows once it has run 150 ms, ticking like a task
|
|
785
|
+
(` 1.2s running internal vx:uploads`); no worker slot or
|
|
777
786
|
`--concurrency` holds it back. When it ends it leaves a list row
|
|
778
|
-
like a task's,
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
787
|
+
like a task's, `internal` in the cache column
|
|
788
|
+
(`⏺︎ 4.60s success internal vx:uploads`); a quicker one shows
|
|
789
|
+
nowhere. A step that runs beside the tasks, `vx:prefetch` (a
|
|
790
|
+
remote's downloads), never shows: `vx last` lists it. `:` is no task
|
|
791
|
+
id's separator, so a step never reads as a task: a project named
|
|
792
|
+
`vx` keeps `vx#build`. No step is a task: no count, bar, history
|
|
793
|
+
row, forecast, flaky verdict or telemetry record holds one;
|
|
794
|
+
`--profile` and `VX_DEBUG_TIMING` time vx's stages their own way.
|
|
795
|
+
Steps show only beside a live region; a log (CI, a pipe) prints
|
|
796
|
+
none.
|
|
785
797
|
5. **The live summary section** — the SAME meters the final footer
|
|
786
798
|
prints (`tasks` + `cache` bars with legends, `time`), filling in as
|
|
787
799
|
the run progresses, under a bare `vx` wordmark rule. A second in,
|
|
@@ -901,14 +913,14 @@ Locally-executed tasks always write in place and ignore the flag, and
|
|
|
901
913
|
it in either direction. **It never affects a cache key**: transfer
|
|
902
914
|
tuning cannot change what a command produces.
|
|
903
915
|
|
|
904
|
-
One safety gate: a task whose outputs another task's `
|
|
916
|
+
One safety gate: a task whose outputs another task's `inputs`
|
|
905
917
|
globs could read on disk is silently kept eager, because deferring it
|
|
906
918
|
would make that key depend on whether the bytes arrived. A task with no
|
|
907
919
|
`cache` that a cached task depends on counts as reading its whole
|
|
908
920
|
project (C-16). `--dry` reports
|
|
909
921
|
how many tasks would keep their outputs remote and names each downgrade,
|
|
910
922
|
so a run that defers nothing says why. A run in which any task declares a
|
|
911
|
-
`
|
|
923
|
+
`inputs.runtime` / `workspaceRuntime` command defers **nothing**:
|
|
912
924
|
a shell command's reads cannot be bounded, so vx cannot prove it will
|
|
913
925
|
not read a deferred output (the same reason vx refuses to infer inputs
|
|
914
926
|
by tracing). When bytes are fetched later, vx saves an ordinary
|
|
@@ -1009,8 +1021,8 @@ $ vx run build lint --affected=main --dry
|
|
|
1009
1021
|
```
|
|
1010
1022
|
|
|
1011
1023
|
`kind` is `input` (a changed `file` is a declared input), `project` (its
|
|
1012
|
-
project changed and the task reads it whole:
|
|
1013
|
-
`file` no
|
|
1024
|
+
project changed and the task reads it whole: a task with no `inputs`, or a
|
|
1025
|
+
changed `file` no task of the project declares, often an output git does not
|
|
1014
1026
|
ignore; no `file` for a config import), `workspace` (a workspace-wide
|
|
1015
1027
|
`file` changed: the workspace config, or a root file a plugin claims such
|
|
1016
1028
|
as a lockfile, reached every task of the project),
|
|
@@ -1086,7 +1098,8 @@ export default {
|
|
|
1086
1098
|
},
|
|
1087
1099
|
},
|
|
1088
1100
|
},
|
|
1089
|
-
|
|
1101
|
+
inputs: { files: ['src/**'] },
|
|
1102
|
+
cache: { outputs: { files: ['dist/**'] } },
|
|
1090
1103
|
},
|
|
1091
1104
|
},
|
|
1092
1105
|
}
|
|
@@ -1110,7 +1123,7 @@ task's `SANDBOX VIOLATIONS` section ends with a dimmed `fix:` line,
|
|
|
1110
1123
|
the grants that would have allowed each denial, project-relative:
|
|
1111
1124
|
`fix: exec.sandbox.allow: { read: ['tsconfig.json'], write: ['dist/'] }`.
|
|
1112
1125
|
A write grants the top directory it landed in, so the next file there
|
|
1113
|
-
passes too, unless `
|
|
1126
|
+
passes too, unless `inputs.files` reads that directory: then it
|
|
1114
1127
|
grants the file alone, so the task cannot write its own inputs; a read the output depends on is a key input as well, and
|
|
1115
1128
|
the line says so, since a grant never adds one. A credential path
|
|
1116
1129
|
(`~/.ssh`, `~/.aws`, `~/.gnupg`, `~/.kube`, `~/.config/gh`,
|
|
@@ -1205,8 +1218,10 @@ run...` precedes it.
|
|
|
1205
1218
|
not change since the loop last saw it is not an edit (a `chmod` is one,
|
|
1206
1219
|
as the key reads the executable bit), nor is a directory
|
|
1207
1220
|
whose entries (names and sizes) did not, nor a path that stayed
|
|
1208
|
-
gone; a symlink is its target string
|
|
1209
|
-
|
|
1221
|
+
gone; a symlink is its target string and what it points to, as the
|
|
1222
|
+
key folds them, so a retarget is an edit, and so is an edit behind
|
|
1223
|
+
it: watch arms each link's target, wherever it lies, and names the
|
|
1224
|
+
link; a path the loop has never judged is an edit only if it was
|
|
1210
1225
|
modified after the watchers went live (macOS delivers the initial
|
|
1211
1226
|
run's own writes after the arm; the later of the path's mtime and
|
|
1212
1227
|
ctime says which side of it a path belongs to, so a file moved in
|
|
@@ -1228,8 +1243,7 @@ run...` precedes it.
|
|
|
1228
1243
|
which reads what it likes, item 947, outside any project nested
|
|
1229
1244
|
in it; a write the last cycle made is not, read off the later of
|
|
1230
1245
|
its mtime and ctime, so an output a cache hit restores with its
|
|
1231
|
-
saved mtime is the run's,
|
|
1232
|
-
file, `watch-restored-output.test.ts`) — or declared an
|
|
1246
|
+
saved mtime is the run's, `watch-restored-output.test.ts`) — or declared an
|
|
1233
1247
|
output, or the loop re-runs on it; after three such cycles in a row
|
|
1234
1248
|
watch names the path and the remedy, once, and keeps going. A dev
|
|
1235
1249
|
server the last cycle left running counts as that cycle for as long
|
|
@@ -1237,7 +1251,7 @@ run...` precedes it.
|
|
|
1237
1251
|
`.gitignore` as the remedy (a persistent task declares no outputs;
|
|
1238
1252
|
item 948). A file git tracks is never blamed on the server: three
|
|
1239
1253
|
saves of a source file are the user's (WD-7). When any project's config declares
|
|
1240
|
-
`
|
|
1254
|
+
`inputs.workspaceFiles`, the per-project watchers are swapped
|
|
1241
1255
|
for ONE recursive root watcher (boundaries are off for those globs,
|
|
1242
1256
|
so a root-relative glob can name a file anywhere). That watcher
|
|
1243
1257
|
hears every write in the tree and keeps only the ones a key can
|
|
@@ -1285,19 +1299,20 @@ Always ignored (no re-trigger):
|
|
|
1285
1299
|
|
|
1286
1300
|
- `node_modules/`, `.git/`, `.vx/cache/` anywhere in the path.
|
|
1287
1301
|
- A path git ignores (`git check-ignore`, asked once per debounce
|
|
1288
|
-
window, never per event)
|
|
1302
|
+
window, never per event), unless a task's inputs name it outright
|
|
1303
|
+
(`.env.local`, `dist/**`): invisible to every other cache key — inputs are
|
|
1289
1304
|
tracked + untracked-not-ignored — so a cycle it started could change
|
|
1290
1305
|
nothing. A tracked file that matches a pattern is not ignored, by
|
|
1291
1306
|
git's own rule; outside a repository nothing is.
|
|
1292
|
-
- Files ending in `~` (editor swap files), and
|
|
1293
|
-
|
|
1307
|
+
- Files ending in `~` (editor swap files), and `.vx/tmp/`, where
|
|
1308
|
+
restores stage.
|
|
1294
1309
|
- The run's **resolved cache directory**, wherever it is. `.vx/cache/` covers the
|
|
1295
1310
|
default, but `defineWorkspace({ cache: { dir } })` and `--cache-dir` can put it
|
|
1296
1311
|
anywhere; watching it would let vx's own cache writes trigger the next
|
|
1297
1312
|
cycle, which writes again — a loop that never settles.
|
|
1298
1313
|
|
|
1299
1314
|
Everything else triggers a cycle. We deliberately don't filter events
|
|
1300
|
-
against per-task `
|
|
1315
|
+
against per-task `inputs.files` — the cache hash is the source
|
|
1301
1316
|
of truth. A change to an irrelevant file produces a cache-hit run
|
|
1302
1317
|
(typically tens of ms); the cost is much smaller than the engineering
|
|
1303
1318
|
cost of a per-event glob match.
|
|
@@ -1863,8 +1878,16 @@ manager is its lockfile (`pnpm-lock.yaml`, `bun.lock`, `yarn.lock`,
|
|
|
1863
1878
|
`package-lock.json`), else `packageManager`, else npm; the field it is
|
|
1864
1879
|
listed in is kept (`--save-dev`, `--dev`, …), and pnpm at a workspace
|
|
1865
1880
|
root adds `--workspace-root`. The lockfile is updated by that manager.
|
|
1866
|
-
vx
|
|
1867
|
-
|
|
1881
|
+
Every `@vzn/vx-*` plugin the same package.json lists from the registry
|
|
1882
|
+
moves in the same install, to the same release, since every @vzn
|
|
1883
|
+
package releases together: `pnpm add @vzn/vx@0.0.2 @vzn/vx-otel@0.0.2
|
|
1884
|
+
--save-dev`. A plugin left behind is installed even when core is
|
|
1885
|
+
already at the release (`› vx upgrade plugins → 0.0.2`); a plugin in
|
|
1886
|
+
another field than core is refused, so no install moves a dependency's
|
|
1887
|
+
field. The versions are pinned, so pnpm's `minimumReleaseAge` refuses a
|
|
1888
|
+
release too young rather than mixing releases, and the error names
|
|
1889
|
+
`minimumReleaseAgeExclude`. vx prints what it runs, runs it there, and
|
|
1890
|
+
exits with an error if it fails:
|
|
1868
1891
|
|
|
1869
1892
|
```
|
|
1870
1893
|
› vx upgrade 0.0.1 → 0.0.2
|
|
@@ -1882,6 +1905,12 @@ project that fails leaves the global as it was. The project's vx run
|
|
|
1882
1905
|
directly (a package script, `node_modules/.bin/vx`) has no global behind it, so only
|
|
1883
1906
|
the project moves.
|
|
1884
1907
|
|
|
1908
|
+
**`vx upgrade -g`** (`--global`) moves only the global vx, wherever it
|
|
1909
|
+
runs: it never hands the call to a project's vx, and it never touches a
|
|
1910
|
+
`package.json` or a plugin. Run as the project's own vx, it refuses:
|
|
1911
|
+
`vx upgrade -g: no global vx ran — <path> is the project's own. Install
|
|
1912
|
+
one (npm install -g @vzn/vx), or run \`vx upgrade\` to move the project's`.
|
|
1913
|
+
|
|
1885
1914
|
```
|
|
1886
1915
|
› vx upgrade project 0.0.1 → 0.0.2
|
|
1887
1916
|
|
|
@@ -1910,9 +1939,11 @@ or is missing.
|
|
|
1910
1939
|
runs in, or one above it up to the git top-level, holds a
|
|
1911
1940
|
`package.json`, `pnpm-workspace.yaml`, `turbo.json`, `turbo.jsonc` or
|
|
1912
1941
|
`nx.json`, it runs `@vzn/vx-migrate` there, so `vx init` is the one
|
|
1913
|
-
command (owner, 2026-10-08): the
|
|
1914
|
-
|
|
1915
|
-
|
|
1942
|
+
command (owner, 2026-10-08): the latest release through `bun x`,
|
|
1943
|
+
never an installed one, so init always installs the latest `@vzn/vx`
|
|
1944
|
+
and moves every `@vzn/vx-*` plugin the root lists to it (M,
|
|
1945
|
+
2026-10-16; a source checkout runs the nearest installed
|
|
1946
|
+
`node_modules/@vzn/vx-migrate` instead) (`BUN_BE_BUN=1` makes the compiled
|
|
1916
1947
|
binary that runtime). `--dry`, `--format`, `--force`, `--mjs`,
|
|
1917
1948
|
`--native` and `--keep` pass through, and the exit is vx-migrate's.
|
|
1918
1949
|
vx-migrate maps a Turbo, Nx or Vite Task repo, else the `package.json`
|
|
@@ -2059,7 +2090,7 @@ copy to the source.
|
|
|
2059
2090
|
| turbo | `--dangerously-disable-package-manager-check` | refuse | vx reads no `packageManager` field: drop it |
|
|
2060
2091
|
| turbo | `--env-mode <v>` | refuse | vx passes only the variables a task declares (strict): list the rest in `exec.env.passThrough` |
|
|
2061
2092
|
| turbo | `--framework-inference <v>` | refuse | under `turbo()` inference is Turbo's; take a name back with a `!` entry in the task's `env` |
|
|
2062
|
-
| turbo | `--global-deps <v>` | refuse | declare them in `
|
|
2093
|
+
| turbo | `--global-deps <v>` | refuse | declare them in `inputs.workspaceFiles` (under `turbo()`, turbo.json's `globalDependencies`) |
|
|
2063
2094
|
| turbo | `--json` | refuse | use `vx last --format json` for the run's result, `--dry=json` for the plan |
|
|
2064
2095
|
| turbo | `--log-file` | refuse | use `vx last --format json` for the run's JSON record |
|
|
2065
2096
|
| turbo | `--preflight` | refuse | set `turboCache({ preflight: true })` or `TURBO_PREFLIGHT=1` |
|
|
@@ -2251,7 +2282,7 @@ its own output path, or make one depend on the other.
|
|
|
2251
2282
|
|
|
2252
2283
|
#### `VX_E_INPUT_IS_OUTPUT`
|
|
2253
2284
|
|
|
2254
|
-
A task's `
|
|
2285
|
+
A task's `inputs` match outputs: its own (vx cleans outputs before
|
|
2255
2286
|
a run, so it would delete its sources) or another task's (its key must
|
|
2256
2287
|
not read them; `dependsOn` already cascades). Exclude the outputs from
|
|
2257
2288
|
the inputs with a `!` glob, as the message shows, or declare no outputs
|
|
@@ -2587,7 +2618,7 @@ app#build — run 01a1193a-7b39-75a9-870b-5c03e26d7104
|
|
|
2587
2618
|
changed file packages/app/src/input.txt 78981922613b2afb6025042ff6bd878ac1994e85 → 61780798228d17af2d34fce4cfbdf35556832472
|
|
2588
2619
|
|
|
2589
2620
|
what to do:
|
|
2590
|
-
file an edit re-runs by design; a file the task does not read belongs out of
|
|
2621
|
+
file an edit re-runs by design; a file the task does not read belongs out of inputs.files
|
|
2591
2622
|
```
|
|
2592
2623
|
|
|
2593
2624
|
A changed key's verdict names what moved, kind and name, three at most
|
|
@@ -2628,7 +2659,7 @@ A hit's line is `cache-hit · key …` (or `cache-hit-remote`): the status
|
|
|
2628
2659
|
names the hit and its tier, so only an executed run carries the word.
|
|
2629
2660
|
A skipped task never ran and derived no key: its line is `skipped · no key`.
|
|
2630
2661
|
A row's kind is what the key folded: `file` (an input file, by blob
|
|
2631
|
-
id), `env` (a declared variable, by digest), `runtime` and
|
|
2662
|
+
id), `linked` (the content behind a `file` that is a symlink), `env` (a declared variable, by digest), `runtime` and
|
|
2632
2663
|
`ws-runtime` (a declared command's output, `inputs.runtime` and
|
|
2633
2664
|
`inputs.workspaceRuntime`), `forward` (the argv forwarded after `--`),
|
|
2634
2665
|
`config` (the evaluated task config), `upstream` (a dependency's input
|
|
@@ -2643,9 +2674,9 @@ is gone (pruned, or the run failed and never saved one) the verb still
|
|
|
2643
2674
|
names the hash change and says the component diff is unavailable. When
|
|
2644
2675
|
only the previous run failed, the diff is taken against the last run
|
|
2645
2676
|
that saved an entry instead, and the detail line names that run. A
|
|
2646
|
-
task with no `cache` block derives a key too — it is what
|
|
2647
|
-
fold — but saves no entry, so for it the verb can only report
|
|
2648
|
-
change and says so.
|
|
2677
|
+
task with `inputs` and no `cache` block derives a key too — it is what
|
|
2678
|
+
dependents fold — but saves no entry, so for it the verb can only report
|
|
2679
|
+
the key change and says so.
|
|
2649
2680
|
`--format json` emits one machine-readable object (`{ taskId, runId,
|
|
2650
2681
|
why, diff, roots, writers }`); when the task's recorded runs predate run ids, `{ taskId,
|
|
2651
2682
|
why, diff, explanation }`, `why` and `diff` null and `explanation` the
|
|
@@ -2687,6 +2718,11 @@ their count (`… +980 more cache hits`) — a thousand-task warm run is a
|
|
|
2687
2718
|
thousand rows otherwise, with the one failure a screen above the
|
|
2688
2719
|
prompt — and the sixteen shown are the slowest restores, the one thing
|
|
2689
2720
|
a hit's row tells. `--format json` lists every row.
|
|
2721
|
+
Below the tasks, the steps the run did not wait on (`vx:prefetch`, a
|
|
2722
|
+
remote's downloads beside the tasks), which its own output never shows:
|
|
2723
|
+
`success vx:prefetch 1.20s internal`. They are kept per run, the
|
|
2724
|
+
newest 50, in `<cache dir>/steps/<run id>.json`, written only by a run
|
|
2725
|
+
that had one; `--format json` adds them as `steps`.
|
|
2690
2726
|
`vx last --list` prints the N most recent runs (default 10), one per
|
|
2691
2727
|
line in aligned columns (verdict, start, run id, counts, duration,
|
|
2692
2728
|
command), each run id cut to the shortest prefix no other run shares, 13 characters at least (`--list 5`
|
|
@@ -2708,7 +2744,7 @@ sandboxed task's row adds `sandboxFix`, the `exec.sandbox.allow`
|
|
|
2708
2744
|
entries (`read`, `write`, `network`, `systemInfo`) that would have
|
|
2709
2745
|
granted its denials, the same grants the terminal prints as `fix:`;
|
|
2710
2746
|
`withheld` lists the credential paths it denied and left out (a `.env` file, or a store under home the sandbox denies, `security.md`), and, for
|
|
2711
|
-
a cached task, `inputs` the reads of its project no `
|
|
2747
|
+
a cached task, `inputs` the reads of its project no `inputs.files`
|
|
2712
2748
|
glob matches (the terminal's `and, if the output depends on them, …`
|
|
2713
2749
|
line). A task
|
|
2714
2750
|
failed by `inputStability.severity: 'error'` adds `inputMoved`, the
|
|
@@ -2894,7 +2930,6 @@ what a TASK sees is `exec.env` in `docs/schema.md`.
|
|
|
2894
2930
|
| `VX_RUN_CONTINUE` | [`run.continue`](schema.md#settings) | What a failed task takes down: `never` stops the run, `deps-ok` skips its dependents, `always` runs them too. |
|
|
2895
2931
|
| `VX_RUN_OUTPUT_LOGS` | [`run.outputLogs`](schema.md#settings) | Which task output the run prints. Chosen before the workspace loads, so flag and env only. |
|
|
2896
2932
|
| `VX_RUN_RULES_EXCLUSIVE_OUTPUTS` | [`run.rules.exclusiveOutputs`](schema.md#settings) | Refuse two tasks whose outputs overlap even when an edge orders them. |
|
|
2897
|
-
| `VX_RUN_RULES_UPFRONT_KEYS` | [`run.rules.upfrontKeys`](schema.md#settings) | Refuse a task whose inputs can match another task's outputs. |
|
|
2898
2933
|
| `VX_CACHE_DIR` | [`cache.dir`](schema.md#settings) | Where the cache lives, relative to the workspace root; named, it holds the whole cache. |
|
|
2899
2934
|
| `VX_CACHE_RETENTION_MAX_SIZE` | [`cache.retention.maxSize`](schema.md#settings) | Evict least-recently-used entries at each run's end until the cache is under this (`'10GB'`). |
|
|
2900
2935
|
| `VX_CACHE_RETENTION_OLDER_THAN` | [`cache.retention.olderThan`](schema.md#settings) | Evict entries unused for this long at each run's end (`'30d'`). |
|
package/docs/execution.md
CHANGED
|
@@ -154,7 +154,7 @@ terminal and a task succeeding or failing. Read it alongside
|
|
|
154
154
|
│ • SIGINT/SIGTERM/SIGHUP handlers (removed in a finally): on
|
|
155
155
|
│ signal, forward it (SIGHUP as SIGTERM) to everything
|
|
156
156
|
│ in liveChildren + persistentRegistry (and kill any
|
|
157
|
-
│ running
|
|
157
|
+
│ running inputs.runtime probe, C-65), wait
|
|
158
158
|
│ 2 s for their groups, SIGKILL
|
|
159
159
|
│ what is still there, let the run finish its own
|
|
160
160
|
│ end (flush, teardown, cache close), then
|
|
@@ -174,12 +174,13 @@ terminal and a task succeeding or failing. Read it alongside
|
|
|
174
174
|
│ overlap is the point); drained before cache.close(). At most
|
|
175
175
|
│ one remote GET per key (shared in-flight map with the lazy
|
|
176
176
|
│ read-through).
|
|
177
|
-
│ • LOCAL SHORT-CIRCUIT (
|
|
178
|
-
│
|
|
179
|
-
│
|
|
180
|
-
│
|
|
181
|
-
│
|
|
182
|
-
│ set (confirmed hits). Awaited
|
|
177
|
+
│ • LOCAL SHORT-CIRCUIT (local reads on, ≥1 task) — derive the
|
|
178
|
+
│ same stable keys and probe the LOCAL store in ONE batched
|
|
179
|
+
│ getMany (a layer without getMany: get once per task under a
|
|
180
|
+
│ bounded pool) → a `preProbed` map (hits AND stable misses;
|
|
181
|
+
│ with a remote declared, hits only: a local miss may be a
|
|
182
|
+
│ remote hit) + a `restoreTier` set (confirmed hits). Awaited
|
|
183
|
+
│ before scheduling; never asks a remote; never throws
|
|
183
184
|
│ (degrades to the normal schedule).
|
|
184
185
|
│
|
|
185
186
|
├─ Scheduling (src/graph/scheduler.ts:runGraph — two-tier)
|
|
@@ -243,7 +244,7 @@ terminal and a task succeeding or failing. Read it alongside
|
|
|
243
244
|
│ UserError that fails this task — its dependents skip,
|
|
244
245
|
│ independent tasks continue)
|
|
245
246
|
│ 2. hashTaskConfig (memoized per run via HashCache)
|
|
246
|
-
│ 3. filterUpstreamHashes (apply
|
|
247
|
+
│ 3. filterUpstreamHashes (apply inputs.tasks filter)
|
|
247
248
|
│ 4. cache.key({ taskId, taskConfigHash,
|
|
248
249
|
│ forwardArgs, envValues, runtimeValues,
|
|
249
250
|
│ workspaceRuntimeValues, upstreamHashes,
|
|
@@ -430,7 +431,7 @@ broader access has cache-stability implications).
|
|
|
430
431
|
| Two projects of one name | `checkProjectNames` throws with both paths |
|
|
431
432
|
| Cycle in task graph | `detectCycle` throws with the cycle path |
|
|
432
433
|
| Malformed config | `loadProjectConfigs` throws (UserError) with file + field |
|
|
433
|
-
| `
|
|
434
|
+
| `inputs.runtime` command exits non-zero | UserError naming the command + exit code; that task is `failed`, dependents skip |
|
|
434
435
|
|
|
435
436
|
By default (`--continue=deps-ok`) failures don't kill the scheduler —
|
|
436
437
|
independent tasks already in flight finish, and unrelated tasks not yet
|
|
@@ -618,7 +619,7 @@ runs the same setup steps — workspace discovery, config load, package
|
|
|
618
619
|
graph, task graph — and the same per-task hash derivation, then
|
|
619
620
|
probes the cache. The probe is read-only; against a remote layer it
|
|
620
621
|
is an existence check only (no artifact download, no ingest). The one
|
|
621
|
-
deliberate side effect: `
|
|
622
|
+
deliberate side effect: `inputs.runtime` / `workspaceRuntime`
|
|
622
623
|
probe commands DO run, because predicting a key requires resolving
|
|
623
624
|
them — keep runtime inputs side-effect-free.
|
|
624
625
|
|
package/docs/features.md
CHANGED
|
@@ -31,7 +31,7 @@ config key or an environment variable is missing here.
|
|
|
31
31
|
- **Schedule strategy** (`--schedule`, `run.schedule`, `VX_RUN_SCHEDULE`) — pick the order with no timings: most work waiting (default), longest chain, most direct dependents, or ready order; plugins still lead. [page](https://vznjs.github.io/vx/features/critical-path/) · post: [Which task runs first?](https://vznjs.github.io/vx/blog/scheduling-strategies/#pick-the-guess)
|
|
32
32
|
- **No daemon** — every run starts cold and still answers in milliseconds. [page](https://vznjs.github.io/vx/features/no-daemon/) · post: [No daemon, on purpose](https://vznjs.github.io/vx/blog/no-daemon/)
|
|
33
33
|
- **Group tasks** (`dependsOn` with no `exec`) — a task that only runs its dependencies; `dependsOn: []` is a named no-op. [page](https://vznjs.github.io/vx/features/group-tasks/) · post: [Say exactly which tasks to run](https://vznjs.github.io/vx/blog/pick-your-tasks/)
|
|
34
|
-
- **Keyed groups** (`
|
|
34
|
+
- **Keyed groups** (`inputs` with no `exec`) — a key a dependant folds: it re-runs on those inputs, and nothing spawns or counts for the group. [page](https://vznjs.github.io/vx/features/group-tasks/) · post: [A key with nothing to run](https://vznjs.github.io/vx/blog/keyed-groups/)
|
|
35
35
|
- **Implicit keyed `build`** — a project with no `build` gets a `^build` group keyed on its files, so source-only packages still move their dependants' keys. [page](https://vznjs.github.io/vx/features/implicit-build/) · post: [The basics, done carefully](https://vznjs.github.io/vx/blog/the-basics/)
|
|
36
36
|
- **dependsOn syntax** (`^name`, `pkg#name`, `name.*`, `^name.*`) — upstream, cross-project and pattern edges; `^` follows the project edges a plugin supplies, and with none is refused naming the fix. [page](https://vznjs.github.io/vx/features/depends-on-syntax/) · post: [Say exactly which tasks to run](https://vznjs.github.io/vx/blog/pick-your-tasks/)
|
|
37
37
|
- **Every name must resolve** — `vx run lint test typecheck` refuses to start if one name matches no project. [page](https://vznjs.github.io/vx/features/names-must-resolve/) · post: [Say exactly which tasks to run](https://vznjs.github.io/vx/blog/pick-your-tasks/)
|
|
@@ -55,7 +55,7 @@ config key or an environment variable is missing here.
|
|
|
55
55
|
- **GitHub Actions log groups** — on Actions, each task's block folds in a `::group::` with its outcome and time. [page](https://vznjs.github.io/vx/features/actions-log-groups/) · post: [The basics, done carefully](https://vznjs.github.io/vx/blog/the-basics/)
|
|
56
56
|
- **Colors** (`NO_COLOR`, `FORCE_COLOR`) — truecolor output, forced on or off by env; `TERM=dumb` prints plain, with no live region. [page](https://vznjs.github.io/vx/features/colors/) · post: [The basics, done carefully](https://vznjs.github.io/vx/blog/the-basics/)
|
|
57
57
|
- **Signal-named exits** — a failure reads `exit 137, 128 + SIGKILL`. [page](https://vznjs.github.io/vx/features/signal-exits/) · post: [When a task misbehaves](https://vznjs.github.io/vx/blog/tasks-that-misbehave/)
|
|
58
|
-
- **vx's own steps as rows** — on a terminal, what vx does around the tasks (`vx:uploads`, `<plugin>:teardown`) shows as a running row once it passes 150 ms, then a list row; never counted as a task. no post
|
|
58
|
+
- **vx's own steps as rows** — on a terminal, what vx does around the tasks (`vx:uploads`, `<plugin>:teardown`) shows as a running row once it passes 150 ms, then a list row marked `internal`, only when the run waits on it (`vx:prefetch` runs beside the tasks and is listed by `vx last` alone); never counted as a task. no post
|
|
59
59
|
- **Plain output off a TTY** — no live region, and a missing task lists tasks instead of opening the picker. [page](https://vznjs.github.io/vx/features/plain-off-tty/) · post: [The basics, done carefully](https://vznjs.github.io/vx/blog/the-basics/)
|
|
60
60
|
|
|
61
61
|
## Plan and explain
|
|
@@ -76,8 +76,8 @@ config key or an environment variable is missing here.
|
|
|
76
76
|
|
|
77
77
|
## Cache
|
|
78
78
|
|
|
79
|
-
- **Opt-in, explicit inputs** (`
|
|
80
|
-
- **Inputs** (`
|
|
79
|
+
- **Opt-in, explicit inputs** (`inputs.files`) — a task is keyed on the inputs it names, cached or not; one with none has no key, so every task behind it misses. A gitignored path named outright (`.env.local`, `dist/**`) is an input, hashed from disk and never stored; broad globs still skip ignored files. [page](https://vznjs.github.io/vx/features/explicit-inputs/) · post: [Explicit over magical](https://vznjs.github.io/vx/blog/explicit-over-magical/)
|
|
80
|
+
- **Inputs** (`inputs.env`, `inputs.runtime`, `inputs.tasks`, `inputs.workspaceFiles`, `inputs.workspaceRuntime`) — env vars, tool versions, upstream tasks and workspace files in the key. [page](https://vznjs.github.io/vx/features/key-inputs/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
|
|
81
81
|
- **Outputs** (`cache.outputs.files`, `cache.outputs.workspaceFiles`) — what a hit restores. [page](https://vznjs.github.io/vx/features/cache-outputs/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
|
|
82
82
|
- **Input stability** (`cache.inputStability`, workspace `cache.inputStability`, `VX_CACHE_INPUT_STABILITY_STRATEGY`, `VX_CACHE_INPUT_STABILITY_SEVERITY`) — an input written while the command runs withholds the save, on CI too, and the saves of every task that ran over its outputs; `strategy: 'mutable'` accepts a task's identical rewrite of its own input, `'unchecked'` skips the check; `severity: 'error'` fails the run instead of warning. no post
|
|
83
83
|
- **Keep outputs on a miss** (`cache.outputs.clean`, workspace `cache.outputs.clean`) — `false` leaves a task's outputs in place for a tool that reads its last output (tsc's `.tsbuildinfo`) and caches everything the outputs then hold, leftovers included (an opt-out of the clean-tree guarantee). no post
|
|
@@ -86,13 +86,14 @@ config key or an environment variable is missing here.
|
|
|
86
86
|
- **Config keyed as evaluated** — the key sees the resolved config object. [page](https://vznjs.github.io/vx/features/typescript-config/) · post: [Configs are programs](https://vznjs.github.io/vx/blog/resolved-config-hashing/)
|
|
87
87
|
- **Cascade through inputs** — a task's key folds its upstream input keys, never outputs. [page](https://vznjs.github.io/vx/features/cascade/) · post: [Cascade through dependencies](https://vznjs.github.io/vx/blog/cascade-through-inputs/)
|
|
88
88
|
- **Lockfile-aware keys** (`@vzn/vx-lockfile`: `pnpm()`, `bun()`, `npm()`, `yarn()`) — a bump re-keys only the projects whose closure changed; each also adds every member with a `package.json` as a project named by its `name`, gives vx the project edges the lockfile records (core reads no `package.json`), keys each package's `package.json` as an input and folds its manager's install settings (`.npmrc`, `bunfig.toml`, `pnpm-workspace.yaml`, `.yarnrc.yml`) into every task. Core keys no package file, so without one an install re-keys nothing; `npm()` reads `npm-shrinkwrap.json` first, and `bun()` refuses a lone `bun.lockb`. [page](https://vznjs.github.io/vx/features/lockfile-keys/) · post: [A lockfile bump should re-key two tasks](https://vznjs.github.io/vx/blog/lockfile-aware-keys/)
|
|
89
|
-
- **Upfront keys** (`
|
|
89
|
+
- **Upfront keys** (`vx lock`, no opt-out) — refuses an input glob another task's outputs could match, so every key is known before anything runs; only a dependency the key does not fold (left out of `inputs.tasks`) may be read, its bytes hashed after it ran. [page](https://vznjs.github.io/vx/features/upfront-keys/) · post: [Cascade through dependencies by folding input keys, never outputs](https://vznjs.github.io/vx/blog/cascade-through-inputs/)
|
|
90
90
|
- **Dependency rules** (`run.rules.dependencies`) — `vx lock` refuses a project depending on one a tag or name rule denies or does not allow, every broken edge named (`VX_E_DEPENDENCY_RULE`). [schema](https://vznjs.github.io/vx/schema/) · post: [Say which projects may depend on which](https://vznjs.github.io/vx/blog/dependency-rules/)
|
|
91
91
|
- **Cache controls** (`--no-cache`, `--force`, `--cache-policy`, `--cache-policy`, `VX_CACHE_POLICY`, `cache.policy`) — off, re-run past it, or per-layer read/write. [page](https://vznjs.github.io/vx/features/cache-controls/) · post: [The cache on your terms](https://vznjs.github.io/vx/blog/the-cache-on-your-terms/)
|
|
92
92
|
- **Cache location** (`--cache-dir`, `cache.dir`, `VX_CACHE_DIR`) — where the cache lives. [page](https://vznjs.github.io/vx/features/cache-location/) · post: [The cache on your terms](https://vznjs.github.io/vx/blog/the-cache-on-your-terms/)
|
|
93
93
|
- **Cache scope** (`cache.scope`, `VX_CACHE_SCOPE`, `--cache-scope`) — a scope reads the default entries, then its own, and writes only its own. [page](https://vznjs.github.io/vx/features/cache-scope/) · post: [The cache on your terms](https://vznjs.github.io/vx/blog/the-cache-on-your-terms/)
|
|
94
94
|
- **Cache pruning** (`vx cache clean`, `--older-than`, `--max-size`, `--global`, `--dry-run`, `cache.retention`, `maxSize`, `olderThan`, `VX_CACHE_RETENTION_MAX_SIZE`, `VX_CACHE_RETENTION_OLDER_THAN`) — evict by age or size, LRU; with no bound, clear this repo's store (shared by its checkouts) and run history; `--global` clears every repo's store. [page](https://vznjs.github.io/vx/features/cache-prune/) · post: [One command to know your workspace](https://vznjs.github.io/vx/blog/know-your-workspace/)
|
|
95
95
|
- **Remote outputs** (`--cache-download`, `cache.download`, `VX_CACHE_DOWNLOAD`) — all, top-level only, or none. [page](https://vznjs.github.io/vx/features/remote-downloads/) · post: [A remote server you can trust in production](https://vznjs.github.io/vx/blog/reapi-in-production/)
|
|
96
|
+
- **Failure uploads** (`cache.upload.failures`) — which runs keep a failed task's log and outputs in the remote: CI (default), local or always. no post
|
|
96
97
|
- **One store for every checkout** (`~/.vx/<id>/cache`) — clones and worktrees of a repo share cache entries. [page](https://vznjs.github.io/vx/features/shared-store/) · post: [The cache on your terms](https://vznjs.github.io/vx/blog/the-cache-on-your-terms/)
|
|
97
98
|
- **Warm hits restore nothing** — when outputs on disk already match, a hit costs a few stats. [page](https://vznjs.github.io/vx/features/warm-hits/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
|
|
98
99
|
- **Hits replay both streams** — stdout and stderr come back in the order the run printed them. [page](https://vznjs.github.io/vx/features/both-streams/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
|
|
@@ -120,7 +121,7 @@ config key or an environment variable is missing here.
|
|
|
120
121
|
- **Interactive tasks** (`exec.interactive`) — a task that owns the terminal. [page](https://vznjs.github.io/vx/features/interactive-tasks/) · post: [When a task misbehaves](https://vznjs.github.io/vx/blog/tasks-that-misbehave/)
|
|
121
122
|
- **Shell completions** (`vx completions`) — bash, zsh, fish. [page](https://vznjs.github.io/vx/features/completions/) · post: [The small things](https://vznjs.github.io/vx/blog/the-small-things/)
|
|
122
123
|
- **Project-local vx** (`VX_HANDED_OFF`) — a global vx hands every call to the project's own `@vzn/vx`. [page](https://vznjs.github.io/vx/cli/#which-vx-runs)
|
|
123
|
-
- **vx upgrade** (`vx upgrade`) — upgrade vx the way it was installed: a verified binary, or its package manager. Inside a project, the project's vx and the global vx that ran it move together. [page](https://vznjs.github.io/vx/features/upgrade/) · post: [An upgrade you can trust](https://vznjs.github.io/vx/blog/upgrade-you-can-trust/)
|
|
124
|
+
- **vx upgrade** (`vx upgrade`) — upgrade vx the way it was installed: a verified binary, or its package manager. Inside a project, the project's vx, its `@vzn/vx-*` plugins (one install, one release) and the global vx that ran it move together; `vx upgrade -g` moves only the global. [page](https://vznjs.github.io/vx/features/upgrade/) · post: [An upgrade you can trust](https://vznjs.github.io/vx/blog/upgrade-you-can-trust/)
|
|
124
125
|
- **Help and version** (`vx help`, `vx version`) — every verb's reference. [page](https://vznjs.github.io/vx/features/help-and-version/) · post: [An upgrade you can trust](https://vznjs.github.io/vx/blog/upgrade-you-can-trust/)
|
|
125
126
|
- **Did-you-mean** — a mistyped flag or verb gets the nearest valid spelling. [page](https://vznjs.github.io/vx/features/did-you-mean/) · post: [The small things](https://vznjs.github.io/vx/blog/the-small-things/)
|
|
126
127
|
- **Task typed as a verb** — `vx build app` answers with the exact `vx run` command that does it. [page](https://vznjs.github.io/vx/features/task-as-verb/) · post: [The small things](https://vznjs.github.io/vx/blog/the-small-things/)
|
|
@@ -144,7 +145,7 @@ config key or an environment variable is missing here.
|
|
|
144
145
|
|
|
145
146
|
## Adoption
|
|
146
147
|
|
|
147
|
-
- **Start in a minute** (`vx init`, `--dry`, `--format json`, `--force`, `--mjs`) — write `.vx/workspace.ts`, one `vx.config.ts` per package (through `@vzn/vx-migrate
|
|
148
|
+
- **Start in a minute** (`vx init`, `--dry`, `--format json`, `--force`, `--mjs`) — write `.vx/workspace.ts`, one `vx.config.ts` per package (through the latest `@vzn/vx-migrate`, which installs the latest `@vzn/vx` and moves every listed `@vzn/vx-*` plugin with it; with no `package.json`, one empty root project) and `.vx/projects.lock.json`; `--dry --format json` hands an agent the plan as data. [page](https://vznjs.github.io/vx/features/quickstart/) · post: [Hello, vx](https://vznjs.github.io/vx/blog/hello-vx/)
|
|
148
149
|
- **Migrate from Turborepo or Nx** (`vx init`, `--native`, `--keep`, `@vzn/vx-migrate`) — native config, or keep `turbo()` / `nx()` as a start. [page](https://vznjs.github.io/vx/features/migrate/) · posts: [From Turborepo](https://vznjs.github.io/vx/blog/from-turborepo/), [From Nx](https://vznjs.github.io/vx/blog/from-nx/)
|
|
149
150
|
- **Keep a Turbo or Nx remote cache** (`turboCache()`, `nxCache()`) — reuse the cache server you have. [page](https://vznjs.github.io/vx/features/keep-remote-cache/) · post: [From Nx: keep the graph, drop the platform](https://vznjs.github.io/vx/blog/from-nx/)
|
|
150
151
|
- **One binary** — one file, nothing to install underneath. [page](https://vznjs.github.io/vx/features/one-binary/) · post: [One binary](https://vznjs.github.io/vx/blog/one-binary/)
|
|
@@ -174,4 +175,4 @@ config key or an environment variable is missing here.
|
|
|
174
175
|
- **OTel live export** (`otel({ live })`) — spans and metrics stream as tasks end, so a dashboard follows a CI run live. [page](https://vznjs.github.io/vx/features/otel-live/) · post: [Watch a CI run while it runs](https://vznjs.github.io/vx/blog/otel-live/)
|
|
175
176
|
- **Memory-aware admission** (`@vzn/vx-schedule-history`) — tasks are packed by the peak memory learned from past runs. [page](https://vznjs.github.io/vx/features/memory-admission/) · post: [A scheduler that learns from your runs](https://vznjs.github.io/vx/blog/a-scheduler-that-learns/)
|
|
176
177
|
- **CI timings file** (`@vzn/vx-schedule-history`, `file`) — one JSON file of learned task times a CI cache carries between runners, so a fresh runner orders by real times. [page](https://vznjs.github.io/vx/features/critical-path/) · post: [Carry the scheduler's memory between CI runners](https://vznjs.github.io/vx/blog/ci-timings-file/)
|
|
177
|
-
- **Shared timings** (`@vzn/vx-schedule-history`, `defaultBranch`) — CI on the default branch writes task timings to the remote cache; a branch with none imports its merge-base's. Plugins keep their own entries there through `ctx.cache`. [page](https://vznjs.github.io/vx/features/critical-path/)
|
|
178
|
+
- **Shared timings** (`@vzn/vx-schedule-history`, `defaultBranch`) — CI on the default branch writes task timings to the remote cache; a branch with none imports its merge-base's without waiting for it, re-ranking the tasks not yet started when they land. Plugins keep their own entries there through `ctx.cache`. [page](https://vznjs.github.io/vx/features/critical-path/)
|
package/docs/patterns.md
CHANGED
|
@@ -44,7 +44,7 @@ package-manager parts come from a plugin.
|
|
|
44
44
|
Core keys no package file (2026-10-14): the lockfile and each
|
|
45
45
|
project's `package.json` key a task through `@vzn/vx-lockfile`, which
|
|
46
46
|
claims the lockfile and adds `package.json` to every cached task's
|
|
47
|
-
inputs, so narrow `
|
|
47
|
+
inputs, so narrow `inputs.files` like `['src/**']` doesn't miss a
|
|
48
48
|
`package.json` dep bump.
|
|
49
49
|
|
|
50
50
|
The two **divergences** from Turbo's recipe — TypeScript-resolved
|