@vzn/vx 0.0.632 → 0.0.633

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.
@@ -0,0 +1,172 @@
1
+ # Every feature
2
+
3
+ Every user-facing feature of vx, one line each, grouped by what it is
4
+ for. Each line names its surface (verb, flag, config key, plugin), links
5
+ its page on the site's [Features](https://vznjs.github.io/vx/features/) hub when it has one, and
6
+ its blog post, or says "no post" when the story is still owed.
7
+
8
+ **Rule:** a change that adds or changes a user-facing feature updates
9
+ this file, and its post, in the same commit (`CLAUDE.md`).
10
+ `tests/features-inventory.unsafe.test.ts` fails when a CLI verb, a flag, a
11
+ config key or an environment variable is missing here.
12
+
13
+ ## Run
14
+
15
+ - **Run a task** (`vx run`) — run a task in the cwd's project, `pkg#task` directly, or several at once. [page](https://vznjs.github.io/vx/features/quickstart/) · post: [The basics, done carefully](https://vznjs.github.io/vx/blog/the-basics/)
16
+ - **Every project** (`--all`) — run the task in every project that declares it. [page](https://vznjs.github.io/vx/features/quickstart/) · post: [The basics, done carefully](https://vznjs.github.io/vx/blog/the-basics/)
17
+ - **pnpm-style filters** (`--filter`) — select projects by name, glob, path, dependencies (`foo...`), dependents (`...foo`), negation or a git range. [page](https://vznjs.github.io/vx/features/affected/) · post: [Say exactly which tasks to run](https://vznjs.github.io/vx/blog/pick-your-tasks/)
18
+ - **Affected only** (`--affected`, `affectedBase`) — run what a change reaches, following task edges from a git base. [page](https://vznjs.github.io/vx/features/affected/) · post: [Run only what a change reaches](https://vznjs.github.io/vx/blog/affected/)
19
+ - **Concurrency** (`--concurrency`, `concurrency`) — a count or a share of the CPUs this process may use (`50%`). [page](https://vznjs.github.io/vx/features/concurrency/) · post: [One failure, and exactly what it takes down](https://vznjs.github.io/vx/blog/when-a-build-fails/)
20
+ - **Skip dependencies** (`--exclude-dependencies`) — skip all `dependsOn` edges, or named ones. [page](https://vznjs.github.io/vx/features/skip-dependencies/) · post: [Say exactly which tasks to run](https://vznjs.github.io/vx/blog/pick-your-tasks/)
21
+ - **Failure policy** (`--continue`) — never, deps-ok (default: dependents skip) or always. [page](https://vznjs.github.io/vx/features/skipped-blockers/) · post: [One failure, and exactly what it takes down](https://vznjs.github.io/vx/blog/when-a-build-fails/)
22
+ - **Every skip names its blocker** — a skipped task says which failure blocked it. [page](https://vznjs.github.io/vx/features/skipped-blockers/) · post: [One failure, and exactly what it takes down](https://vznjs.github.io/vx/blog/when-a-build-fails/)
23
+ - **Retries** (`--retry`, `exec.retries`) — re-run a failed task; a pass after a failure marks it flaky. [page](https://vznjs.github.io/vx/features/flaky-detection/) · post: [Flaky is a claim only declared inputs can back](https://vznjs.github.io/vx/blog/flaky-tasks/)
24
+ - **Timeouts** (`--timeout`, `exec.timeout`, `timeout`, `VX_TASK_TIMEOUT`) — kill and fail a runaway task. [page](https://vznjs.github.io/vx/features/timeouts/) · post: [When a task misbehaves](https://vznjs.github.io/vx/blog/tasks-that-misbehave/)
25
+ - **Argument forwarding** (`--`) — args after `--` reach the task's command and fold into its key. [page](https://vznjs.github.io/vx/features/forward-args/) · post: [When a task misbehaves](https://vznjs.github.io/vx/blog/tasks-that-misbehave/)
26
+ - **Task picker** — `vx run` with no task in a terminal lists tasks to pick. [page](https://vznjs.github.io/vx/features/task-picker/) · post: [The small things](https://vznjs.github.io/vx/blog/the-small-things/)
27
+ - **Typo hints** — an unknown task, project or filter suggests the nearest name. [page](https://vznjs.github.io/vx/features/filter-hints/) · post: [The small things](https://vznjs.github.io/vx/blog/the-small-things/)
28
+ - **Turbo and Nx spellings** (`-t`, `-p`, `--exclude`, `--parallel`, `--base`, `--dry-run`, `--skip-nx-cache`) — accepted, or refused naming the vx spelling. [page](https://vznjs.github.io/vx/features/turbo-nx-flags/) · post: [Flags you already know](https://vznjs.github.io/vx/blog/flags-you-already-know/)
29
+ - **Ctrl-C leaves nothing running** (`VX_KILL_GRACE_MS`, `VX_TEARDOWN_TIMEOUT_MS`) — the whole process tree stops, then teardown runs. [page](https://vznjs.github.io/vx/features/ctrl-c/) · post: [Ctrl-C leaves nothing running](https://vznjs.github.io/vx/blog/ctrl-c/)
30
+ - **Longest chain first** — the scheduler starts the critical path first; `@vzn/vx-schedule-history` learns it from past runs. [page](https://vznjs.github.io/vx/features/critical-path/) · post: [Bitsets, popcount, and a scheduler tick](https://vznjs.github.io/vx/blog/bitsets-and-the-scheduler/)
31
+ - **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/)
32
+ - **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/)
33
+ - **Keyed groups** (`cache` 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/)
34
+ - **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/)
35
+ - **dependsOn syntax** (`^name`, `pkg#name`, `name.*`, `^name.*`) — upstream, cross-project and pattern edges. [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/)
36
+ - **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/)
37
+ - **Tag filters** (`--filter tag:<pattern>`) — select projects by their config `tags`. [page](https://vznjs.github.io/vx/features/tag-filters/) · post: [Say exactly which tasks to run](https://vznjs.github.io/vx/blog/pick-your-tasks/)
38
+ - **Directory and root filters** (`./<dir>`, `{<dir>}`, `.`, `//`) — select projects by path, or the root project. [page](https://vznjs.github.io/vx/features/dir-filters/) · post: [Say exactly which tasks to run](https://vznjs.github.io/vx/blog/pick-your-tasks/)
39
+ - **Executor pools** (executor `capacity`) — a remote pool is admitted against its own width, not the laptop's cores. [page](https://vznjs.github.io/vx/features/executor-pools/) · post: [Remote execution without moving the scheduler](https://vznjs.github.io/vx/blog/remote-execution/)
40
+ - **Runs take turns** — two vx runs on one workspace wait on a lock and name who they wait for. [page](https://vznjs.github.io/vx/features/run-lock/) · post: [When a task misbehaves](https://vznjs.github.io/vx/blog/tasks-that-misbehave/)
41
+
42
+ ## Output
43
+
44
+ - **Framed output** — each task's log in its own frame, never interleaved. [page](https://vznjs.github.io/vx/features/framed-output/) · post: [A run you can read](https://vznjs.github.io/vx/blog/a-run-you-can-read/)
45
+ - **Output modes** (`--output-logs`) — full, errors-only, hash-only or none; the default follows the flow. [page](https://vznjs.github.io/vx/features/output-modes/) · post: [Output that fits the run](https://vznjs.github.io/vx/blog/output-that-fits-the-run/)
46
+ - **Run summary** — one block: projects, tasks, cache, time; nothing prints below it. [page](https://vznjs.github.io/vx/features/run-summary/) · post: [A run you can read](https://vznjs.github.io/vx/blog/a-run-you-can-read/)
47
+ - **Per-task table** (`--verbosity`) — a per-task summary after the run. [page](https://vznjs.github.io/vx/features/per-task-table/) · post: [One failure, and exactly what it takes down](https://vznjs.github.io/vx/blog/when-a-build-fails/)
48
+ - **Failed output kept for agents** — a failure's full log is saved and pointed to. [page](https://vznjs.github.io/vx/features/failed-output-kept/) · post: [Built for the agent at the keyboard](https://vznjs.github.io/vx/blog/built-for-agents/)
49
+ - **Markdown report** (`--report`, `--report-file`) — a run report for a PR or `$GITHUB_STEP_SUMMARY`. [page](https://vznjs.github.io/vx/features/markdown-report/) · post: [Output that fits the run](https://vznjs.github.io/vx/blog/output-that-fits-the-run/)
50
+ - **Run JSON** (`--summarize`) — per-run JSON for scripts, with the time the cache saved (`savedMs`). [page](https://vznjs.github.io/vx/features/run-json/) · post: [See inside a run](https://vznjs.github.io/vx/blog/see-inside-a-run/)
51
+ - **Trace profile** (`--profile`) — Chrome-trace JSON of the run. [page](https://vznjs.github.io/vx/features/profile/) · post: [Output that fits the run](https://vznjs.github.io/vx/blog/output-that-fits-the-run/)
52
+ - **Run tags** (`--tag`) — label a run; recorded in history. [page](https://vznjs.github.io/vx/features/run-tags/) · post: [See inside a run](https://vznjs.github.io/vx/blog/see-inside-a-run/)
53
+ - **Stage timing** (`VX_TIMING`) — vx's own stage table, for performance work. [page](https://vznjs.github.io/vx/features/stage-timing/) · post: [See inside a run](https://vznjs.github.io/vx/blog/see-inside-a-run/)
54
+ - **Output flows** — what is printed follows the run's intent (focused, broad or CI); a truthy `CI` wins. [page](https://vznjs.github.io/vx/features/output-flows/) · post: [Output that fits the run](https://vznjs.github.io/vx/blog/output-that-fits-the-run/)
55
+ - **Cache-aware glyphs** — each task line's glyph shows ran, fresh, restored locally or remotely, failed, skipped or persistent. [page](https://vznjs.github.io/vx/features/cache-glyphs/) · post: [Output that fits the run](https://vznjs.github.io/vx/blog/output-that-fits-the-run/)
56
+ - **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/)
57
+ - **Colors** (`NO_COLOR`, `FORCE_COLOR`) — truecolor output, forced on or off by env. [page](https://vznjs.github.io/vx/features/colors/) · post: [The basics, done carefully](https://vznjs.github.io/vx/blog/the-basics/)
58
+ - **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/)
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
+
61
+ ## Plan and explain
62
+
63
+ - **Dry run** (`--dry`) — the task graph and the predicted hits and misses, text or JSON (with the predicted wall time and its critical path), nothing runs. [page](https://vznjs.github.io/vx/features/dry-run/) · post: [See the plan before you run it](https://vznjs.github.io/vx/blog/dry-run/)
64
+ - **Graph** (`--graph`) — the task graph as Graphviz DOT. [page](https://vznjs.github.io/vx/features/task-graph/) · post: [See inside a run](https://vznjs.github.io/vx/blog/see-inside-a-run/)
65
+ - **vx why** (`vx why`, `--run`) — why a task re-ran, down to the file, env var or config that changed. [page](https://vznjs.github.io/vx/features/vx-why/) · post: [Why did this re-run?](https://vznjs.github.io/vx/blog/why-did-this-rerun/)
66
+ - **vx show** (`vx show`) — every project, a project's or a task's live resolved config. [page](https://vznjs.github.io/vx/features/vx-show/) · post: [See what a task really is](https://vznjs.github.io/vx/blog/vx-show/)
67
+ - **vx last** (`vx last`, `--list`, `--failed`, `--log`) — replay a recorded run's summary, list recent runs, or print one task's output. [page](https://vznjs.github.io/vx/features/vx-last/) · post: [The last run, on request](https://vznjs.github.io/vx/blog/vx-last/)
68
+ - **vx info** (`vx info`) — workspace doctor: versions, projects, cache size against `cacheRetention.maxSize`. [page](https://vznjs.github.io/vx/features/vx-info/) · post: [One command to know your workspace](https://vznjs.github.io/vx/blog/know-your-workspace/)
69
+ - **JSON everywhere** (`--format`) — `show`, `info`, `why`, `last` and `cache` print JSON for agents. [page](https://vznjs.github.io/vx/features/json-everywhere/) · post: [Built for the agent at the keyboard](https://vznjs.github.io/vx/blog/built-for-agents/)
70
+ - **Run analytics in SQLite** — every task's time, CPU, peak memory and cache result, queryable with `sqlite3`. [page](https://vznjs.github.io/vx/features/run-analytics/) · post: [Built for the agent at the keyboard](https://vznjs.github.io/vx/blog/built-for-agents/)
71
+ - **Stable error codes** (`VX_E_…`) — under `--format json` a refusal is a JSON line on stdout with a code an agent branches on. [page](https://vznjs.github.io/vx/features/error-codes/) · post: [A refusal an agent can read](https://vznjs.github.io/vx/blog/error-codes/)
72
+ - **Shipped JSON Schemas** (`@vzn/vx/schemas/`) — every `--format json` shape has a schema, and stdout always parses. [page](https://vznjs.github.io/vx/features/json-schemas/) · post: [Built for the agent at the keyboard](https://vznjs.github.io/vx/blog/built-for-agents/)
73
+ - **Docs for agents** (`llms.txt`, `llms-full.txt`) — the whole site as markdown for coding agents. [page](https://vznjs.github.io/vx/features/llms-txt/) · post: [Built for the agent at the keyboard](https://vznjs.github.io/vx/blog/built-for-agents/)
74
+ - **vx docs** (`vx docs`, `--limit`) — search the CLI, config and cache reference offline; it ships inside vx. [page](https://vznjs.github.io/vx/cli/#vx-docs) · post: [The reference, offline](https://vznjs.github.io/vx/blog/vx-docs-offline/)
75
+ - **Affected reasons** (`--affected --dry`) — each kept task says the changed file or the dependency chain that reached it, text or JSON. [page](https://vznjs.github.io/vx/features/affected-reasons/) · post: [See inside a run](https://vznjs.github.io/vx/blog/see-inside-a-run/)
76
+ - **Agent skill** (`skills/vx/SKILL.md`) — an installable skill that teaches a coding agent to run, debug and query vx. [page](https://vznjs.github.io/vx/features/agent-skill/) · post: [Built for the agent at the keyboard](https://vznjs.github.io/vx/blog/built-for-agents/)
77
+
78
+ ## Cache
79
+
80
+ - **Opt-in, explicit inputs** (`cache.inputs.files`) — a task caches only when it names its inputs. [page](https://vznjs.github.io/vx/features/explicit-inputs/) · post: [Explicit over magical](https://vznjs.github.io/vx/blog/explicit-over-magical/)
81
+ - **Inputs** (`cache.inputs.env`, `cache.inputs.runtime`, `cache.inputs.tasks`, `cache.inputs.workspaceFiles`, `cache.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/)
82
+ - **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/)
83
+ - **Outputs are exactly the snapshot** (`exclusiveOutputs`) — a hit leaves the tree as the run did; two tasks may not own one output without an edge. [page](https://vznjs.github.io/vx/features/strict-outputs/) · post: [The tree is exactly the snapshot](https://vznjs.github.io/vx/blog/strict-output-ownership/)
84
+ - **Keys from git's index** — tracked clean files hash by their blob id, no read. [page](https://vznjs.github.io/vx/features/keys-from-git/) · post: [Your cache key is already in git's index](https://vznjs.github.io/vx/blog/keys-from-git/)
85
+ - **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/)
86
+ - **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/)
87
+ - **Lockfile-aware keys** (`@vzn/vx-lockfile`: `pnpm()`, `bun()`, `npm()`, `yarn()`) — a bump re-keys only the projects whose closure changed. [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/)
88
+ - **Upfront keys** (`upfrontKeys`) — refuses an input glob a same-project task's outputs could match, so every key is known before anything runs. [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/)
89
+ - **Cache controls** (`--no-cache`, `--force`, `--cache`) — off, refresh, 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/)
90
+ - **Cache location** (`--cache-dir`, `cacheDir`, `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/)
91
+ - **Cache scope** (`cacheScope`, `VX_CACHE_SCOPE`) — trusted CI writes the remote cache; a laptop reads it. [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/)
92
+ - **Cache pruning** (`vx cache prune`, `--older-than`, `--max-size`, `--dry-run`, `cacheRetention`, `maxSize`, `olderThan`) — evict by age or size, LRU. [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/)
93
+ - **Remote outputs** (`--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/)
94
+ - **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/)
95
+ - **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/)
96
+ - **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/)
97
+ - **Restore lane** — cache restores run on their own lane, up to twice `--concurrency`. [page](https://vznjs.github.io/vx/features/restore-lane/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
98
+ - **Config evaluation cache** — provably pure `vx.config.ts` files are read back as data, not evaluated again. [page](https://vznjs.github.io/vx/features/config-cache/) · post: [Guard rails that tell you the fix](https://vznjs.github.io/vx/blog/guard-rails/)
99
+ - **Line-ending-correct keys** — files git filters (`eol`, `core.autocrlf`) key on the bytes the build sees. [page](https://vznjs.github.io/vx/features/line-endings/) · post: [Your cache key is already in git's index](https://vznjs.github.io/vx/blog/keys-from-git/)
100
+ - **Background remote uploads** — remote writes drain at the end of the run and never fail the build. [page](https://vznjs.github.io/vx/features/background-uploads/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
101
+ - **Bring your own remote cache** (plugin `cache`) — plug any cache server in through one interface. [page](https://vznjs.github.io/vx/features/custom-remote-cache/) · post: [Extend vx in an afternoon](https://vznjs.github.io/vx/blog/extend-vx/)
102
+
103
+ ## Correctness
104
+
105
+ - **Sandboxed tasks** (`exec.sandbox`: `allow`, `deny`, `ignore`, `weakerNetworkIsolation`, `weakerWhenNested`) — a task reads and writes only what it declares; `allow` names `read`, `write`, `network`, `unixSockets`, `localBinding`, `pty`, `systemInfo`, `gitConfig`, `machLookup`. [page](https://vznjs.github.io/vx/features/sandbox/) · post: [The sandbox](https://vznjs.github.io/vx/blog/the-sandbox/)
106
+ - **Env isolation** (`exec.env`: `define`, `passThrough`, `secret`) — a task sees only the env it names; secrets are masked. [page](https://vznjs.github.io/vx/features/env-isolation/) · post: [A task sees only the env it names](https://vznjs.github.io/vx/blog/env-isolation/)
107
+ - **vx lock** (`vx lock`, `--check`, `--format json`, `--frozen`) — freeze what configs evaluate to; CI and agents check it. [page](https://vznjs.github.io/vx/features/vx-lock/) · post: [vx lock: freezing what the key sees](https://vznjs.github.io/vx/blog/lock-and-frozen/)
108
+ - **Flaky detection** — a task that fails then passes is reported flaky. [page](https://vznjs.github.io/vx/features/flaky-detection/) · post: [Flaky is a claim](https://vznjs.github.io/vx/blog/flaky-tasks/)
109
+ - **Project boundaries** — globs never cross into another project. [page](https://vznjs.github.io/vx/features/project-boundaries/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
110
+ - **Artifact integrity checks** — a CRC-32, a key match and an outputs-only check on every artifact; damage is a miss. [page](https://vznjs.github.io/vx/features/artifact-integrity/) · post: [What goes into a key, and what comes back](https://vznjs.github.io/vx/blog/inside-a-cache-hit/)
111
+ - **Sandbox names what to grant** — a refused write is named beside the failed task with the path to allow. [page](https://vznjs.github.io/vx/features/sandbox-grants/) · post: [Guard rails that tell you the fix](https://vznjs.github.io/vx/blog/guard-rails/)
112
+ - **Strict numeric flags** — `0x10`, `1e3` and `2.7` are refused, never reinterpreted. [page](https://vznjs.github.io/vx/features/strict-numbers/) · post: [An upgrade you can trust](https://vznjs.github.io/vx/blog/upgrade-you-can-trust/)
113
+ - **Verified releases** — binaries carry provenance, `vx upgrade` checks SHA-256, npm publishes with provenance. [page](https://vznjs.github.io/vx/features/verified-releases/) · post: [An upgrade you can trust](https://vznjs.github.io/vx/blog/upgrade-you-can-trust/)
114
+
115
+ ## Daily work
116
+
117
+ - **Watch mode** (`vx watch`, `VX_WATCH_POLL`) — re-run what a change affects, on content, not events. [page](https://vznjs.github.io/vx/features/watch/) · post: [Watch: a content gate](https://vznjs.github.io/vx/blog/watch-mode/)
118
+ - **Dev servers in the graph** (`exec.persistent`, `readyWhen`, `VX_READY_NOTICE_MS`) — a server is a node; dependents start when it is ready. [page](https://vznjs.github.io/vx/features/dev-servers/) · post: [Dev servers as graph nodes](https://vznjs.github.io/vx/blog/dev-servers-in-the-graph/)
119
+ - **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/)
120
+ - **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/)
121
+ - **vx upgrade** (`vx upgrade`) — replace the binary with a release. [page](https://vznjs.github.io/vx/features/upgrade/) · post: [An upgrade you can trust](https://vznjs.github.io/vx/blog/upgrade-you-can-trust/)
122
+ - **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/)
123
+ - **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/)
124
+ - **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/)
125
+
126
+ ## Config
127
+
128
+ - **Config in TypeScript** (`vx.config.ts`, `defineProject`, `vx.workspace.ts`, `defineWorkspace`) — typed, composable; no named inputs. [page](https://vznjs.github.io/vx/features/typescript-config/) · post: [Config in TypeScript](https://vznjs.github.io/vx/blog/config-in-typescript/)
129
+ - **Tasks** (`tasks`, `exec.command`, `dependsOn`, `description`, `tags`) — one command per task; the shell is the API. [page](https://vznjs.github.io/vx/features/one-command-tasks/) · post: [One command per task](https://vznjs.github.io/vx/blog/one-command-per-task/)
130
+ - **Workspace rules** (`rules`) — speed-only checks, on by default, configurable. [page](https://vznjs.github.io/vx/features/workspace-rules/) · post: [Guard rails that tell you the fix](https://vznjs.github.io/vx/blog/guard-rails/)
131
+ - **Config worker timeout** (`VX_CONFIG_WORKER_TIMEOUT_MS`) — bound a config's evaluation. [page](https://vznjs.github.io/vx/features/config-timeout/) · post: [Guard rails that tell you the fix](https://vznjs.github.io/vx/blog/guard-rails/)
132
+ - **No nested runs** (`VX_RUN_TASK`, `VX_RUN_WORKSPACE`) — set on every task; a `vx run` inside a task of the same workspace is refused. [page](https://vznjs.github.io/vx/features/no-nested-runs/) · post: [When a task misbehaves](https://vznjs.github.io/vx/blog/tasks-that-misbehave/)
133
+ - **Typed config helpers** (`defineProject`) — autocomplete for task names in `dependsOn`, errors while you edit. [page](https://vznjs.github.io/vx/features/typed-helpers/) · post: [Config in TypeScript, and why there are no named inputs](https://vznjs.github.io/vx/blog/config-in-typescript/)
134
+ - **Presets** — a TypeScript function returning a task config, shared across projects. [page](https://vznjs.github.io/vx/features/presets/) · post: [Config in TypeScript, and why there are no named inputs](https://vznjs.github.io/vx/blog/config-in-typescript/)
135
+ - **Clean config errors** — a bad config fails at load naming the key, with no stack trace. [page](https://vznjs.github.io/vx/features/config-errors/) · post: [When a task misbehaves](https://vznjs.github.io/vx/blog/tasks-that-misbehave/)
136
+
137
+ ## CI
138
+
139
+ - **Results on GitHub** (`@vzn/vx-ci`: `github()`) — job summary and Checks API annotations. [page](https://vznjs.github.io/vx/features/github-ci/) · post: [Your run, on the pull request](https://vznjs.github.io/vx/blog/results-on-github/)
140
+ - **PR check run** (`github({ checks })`) — a check run on the commit with the run summary as its output. [page](https://vznjs.github.io/vx/features/pr-check-run/) · post: [Your run, on the pull request](https://vznjs.github.io/vx/blog/results-on-github/)
141
+ - **Cache scope from the ref** (`github({ cacheScope })`) — main writes trusted keys; a PR writes only its own scope. [page](https://vznjs.github.io/vx/features/ref-cache-scope/) · post: [The cache on your terms](https://vznjs.github.io/vx/blog/the-cache-on-your-terms/)
142
+
143
+ ## Adoption
144
+
145
+ - **Start in a minute** (`vx init`, `--dry`, `--format json`, `--force`, `--mjs`) — write `vx.workspace.ts` and one `vx.config.ts` per package; `--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/)
146
+ - **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/)
147
+ - **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/)
148
+ - **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/)
149
+ - **The playground** — vx's planner in the browser. [page](https://vznjs.github.io/vx/features/playground/) · post: [Try the planner in your browser](https://vznjs.github.io/vx/blog/the-playground/)
150
+ - **Benchmarks you can re-run** (`@vzn/vx-bench`) — vx against Turborepo and Nx. [page](https://vznjs.github.io/vx/features/fastest/) · posts: [Benchmarks you can re-run](https://vznjs.github.io/vx/blog/honest-benchmarks/), [Why vx is fast](https://vznjs.github.io/vx/blog/why-vx-is-fast/)
151
+ - **npm pre/post scripts** — `pre<x>` and `post<x>` hooks fold into `x`'s command when `vx init` maps scripts. [page](https://vznjs.github.io/vx/features/npm-hooks/) · post: [From npm scripts or Vite Task](https://vznjs.github.io/vx/blog/from-scripts-and-vite-task/)
152
+ - **Vite Task adoption** (`bunx @vzn/vx-migrate`) — writes configs from vite-plus `run.tasks` as well as Turbo and Nx. [page](https://vznjs.github.io/vx/features/vite-task-adoption/) · post: [From npm scripts or Vite Task](https://vznjs.github.io/vx/blog/from-scripts-and-vite-task/)
153
+ - **Nx executors as one process** (`nx-exec`) — any Nx executor runs as one vx task with its Nx env set. [page](https://vznjs.github.io/vx/features/nx-exec/) · post: [From Nx: keep the graph, drop the platform](https://vznjs.github.io/vx/blog/from-nx/)
154
+ - **Programmatic API** (`run`, `planRun`) — run or plan from your own scripts via `@vzn/vx`. [page](https://vznjs.github.io/vx/features/programmatic-api/) · post: [Extend vx in an afternoon](https://vznjs.github.io/vx/blog/extend-vx/)
155
+
156
+ ## Plugins
157
+
158
+ - **A pipeline with seams** (`plugins`, `definePlugin`; stages `config` `discover` `project` `graph` `key` `fingerprint` `schedule` `admit` `executor` `cache` `telemetry` `commands`) — every stage is a plugin seam. [page](https://vznjs.github.io/vx/features/plugins/) · post: [A pipeline with seams](https://vznjs.github.io/vx/blog/pipeline-with-seams/)
159
+ - **Write a plugin** (`vx init --plugin`) — scaffold a runnable plugin for a seam. [page](https://vznjs.github.io/vx/features/plugins/) · post: [Extend vx in an afternoon](https://vznjs.github.io/vx/blog/extend-vx/)
160
+ - **The local floor** — running and caching here are core's, not plugins. [page](https://vznjs.github.io/vx/features/local-floor/) · post: [The local floor](https://vznjs.github.io/vx/blog/the-local-floor/)
161
+ - **Remote cache and execution** (`@vzn/vx-reapi`, `exec.remote`) — Bazel REAPI cache and workers; the scheduler stays here. [page](https://vznjs.github.io/vx/features/remote-execution/) · post: [Remote execution without moving the scheduler](https://vznjs.github.io/vx/blog/remote-execution/)
162
+ - **OpenTelemetry** (`@vzn/vx-otel`) — each run exported to your OpenTelemetry backend, never breaking it. [page](https://vznjs.github.io/vx/features/opentelemetry/) · post: [Observability that cannot break a run](https://vznjs.github.io/vx/blog/telemetry-never-breaks-a-run/)
163
+ - **vx mcp** (`@vzn/vx-mcp`, `vx mcp`) — cache stats, run history, `vx why`'s full answer, a run tool (`mcp({ run })` limits it to named tasks or turns it off), a plan tool, a task's resolved config, the lock audit, cache pruning, the `vx init` plan, an offline docs search and a task list narrowed by `--filter` / `--affected` for coding agents. [page](https://vznjs.github.io/vx/features/mcp/) · post: [Give your coding agent the build's memory](https://vznjs.github.io/vx/blog/agents-and-mcp/)
164
+ - **vx prune** (`@vzn/vx-lockfile`, `vx prune`, `--production`) — copy projects and their deps, lockfile pruned, for a Docker build; `--production` leaves out what only dev dependencies need. [page](https://vznjs.github.io/vx/features/vx-prune/) · post: [Ship one app, not the whole monorepo](https://vznjs.github.io/vx/blog/vx-prune/), [A runtime image without the dev tools](https://vznjs.github.io/vx/blog/prune-production/)
165
+ - **vx history** (`@vzn/vx-schedule-history`, `vx history`) — what the scheduler learned per task. [page](https://vznjs.github.io/vx/features/vx-history/) · post: [A scheduler that learns from your runs](https://vznjs.github.io/vx/blog/a-scheduler-that-learns/)
166
+ - **Setup and teardown hooks** (`setup`, `teardown`) — plugin code around the run, bounded by a timeout. [page](https://vznjs.github.io/vx/features/setup-teardown/) · post: [Extend vx in an afternoon](https://vznjs.github.io/vx/blog/extend-vx/)
167
+ - **REAPI TLS, mTLS and headers** — connect to hosted servers such as BuildBuddy the way Bazel does. [page](https://vznjs.github.io/vx/features/reapi-tls/) · post: [A remote server you can trust in production](https://vznjs.github.io/vx/blog/reapi-in-production/)
168
+ - **REAPI execution records** — a repeat remote execution skips the worker and replays outputs and stdout. [page](https://vznjs.github.io/vx/features/reapi-action-cache/) · post: [A remote server you can trust in production](https://vznjs.github.io/vx/blog/reapi-in-production/)
169
+ - **REAPI verified downloads and deadlines** — a corrupt blob or a wedged server degrades to a miss, never a hang. [page](https://vznjs.github.io/vx/features/reapi-safety/) · post: [A remote server you can trust in production](https://vznjs.github.io/vx/blog/reapi-in-production/)
170
+ - **Install as a remote action** (`exec.remote: 'only'`) — `node_modules` is built by an action, so stateless workers have it. [page](https://vznjs.github.io/vx/features/remote-install/) · post: [Remote execution without moving the scheduler](https://vznjs.github.io/vx/blog/remote-execution/)
171
+ - **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/)
172
+ - **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/)
@@ -0,0 +1,219 @@
1
+ # Shared patterns with Turborepo and Nx
2
+
3
+ The companion to [`comparison.md`](./comparison.md). That doc lists
4
+ what Turbo / Nx do that vx doesn't. This one lists what we deliberately
5
+ inherited or independently converged on — the parts a Turbo or Nx user
6
+ should find familiar.
7
+
8
+ Every row cites the vx source file that implements the pattern; where
9
+ a comment on the line names the upstream tool, that's the explicit
10
+ parity claim.
11
+
12
+ ## The 30-second view
13
+
14
+ vx's overall shape is **Turborepo with three swaps and a few subtractions**:
15
+
16
+ - per-package config (Turbo's `turbo.json` → vx's `vx.config.ts`),
17
+ - opt-in cache keyed by inputs + env + upstream + lockfile,
18
+ - shell-only task contract,
19
+ - tar artifact + a plugin-driven remote-cache layer,
20
+ - topological scheduler with bounded parallelism.
21
+
22
+ The swaps: TypeScript config instead of JSON, resolved-config hash
23
+ instead of file hash, strict output ownership instead of additive
24
+ restore. Everything else lines up with Turbo or Nx by design.
25
+
26
+ ## Subsystem-by-subsystem
27
+
28
+ ### Cache key composition
29
+
30
+ The set of bytes fed into the hash mirrors Turbo's key recipe with one
31
+ Nx-borrowed extension.
32
+
33
+ | Component | Turbo | Nx | vx | vx source |
34
+ | -------------------------------------- | ------------ | ---------------------- | ---------- | --------------------------------------------- |
35
+ | Task identity (`project#task`) | yes | yes | yes | `src/cache/layer.ts` (`CacheKeyInput.taskId`) |
36
+ | Resolved command + env declarations | yes | yes | yes | `src/cache/layer.ts` (`taskConfigHash`) |
37
+ | Declared input file contents | yes | yes | yes | `src/cache/layer.ts` (`inputFiles`) |
38
+ | Declared env-var **values** at runtime | yes | yes | yes | `src/cache/layer.ts` (`envValues`) |
39
+ | Upstream task hashes (cascade) | yes | yes | yes | `src/cache/layer.ts` (`upstreamHashes`) |
40
+ | Workspace lockfile fingerprint | yes | yes | yes | `src/cache/layer.ts` (`workspaceFingerprint`) |
41
+ | Forwarded CLI args (after `--`) | yes | yes | yes | `src/cache/layer.ts` (`forwardArgs`) |
42
+ | Project `package.json` bytes (direct) | via lockfile | `externalDependencies` | **direct** | `src/cache/layer.ts` ("Turbo / Nx parity") |
43
+
44
+ The `package.json` fold is the Nx-borrowed move (Turbo gets it
45
+ transitively through the lockfile; Nx's `externalDependencies` does it
46
+ explicitly). We fold the bytes directly so narrow `cache.inputs.files`
47
+ like `['src/**']` doesn't miss a `package.json` dep bump.
48
+
49
+ The two **divergences** from Turbo's recipe — TypeScript-resolved
50
+ config hash and per-task forwarded-args fold — are documented in
51
+ [`caching.md`](./caching.md) and listed under "Where vx is ahead" in
52
+ [`comparison.md`](./comparison.md).
53
+
54
+ ### Input enumeration
55
+
56
+ | Pattern | Source | vx source |
57
+ | -------------------------------------------------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------- |
58
+ | Defer to git: tracked from `git ls-files -s -v`, untracked-but-not-ignored from `git status --porcelain -uall` | Turbo + Nx | `src/cache/git-inputs.ts`, `src/cache/inputs.ts` ("same as Turbo and Nx") |
59
+ | Git blob OIDs as per-file content hashes (index-harvested) | Turbo | `src/cache/git-inputs.ts` + `src/cache/file-hashes.ts` |
60
+ | Project-boundary enforcement (no cross-project globs) | Turbo + Nx | `src/workspace/nested-dirs.ts` |
61
+
62
+ (vx hard-requires git — there is no fallback walker; a non-repo
63
+ workspace gets a clean `UserError` telling the user to `git init`.)
64
+
65
+ ### `dependsOn` micro-syntax
66
+
67
+ The string DSL is verbatim Turbo (Nx supports the same forms plus a
68
+ richer object form).
69
+
70
+ | Form | Meaning | Source | vx source |
71
+ | ------------ | ----------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------ |
72
+ | `'lint'` | Same project, other task | Turbo + Nx | `src/graph/dependency-spec.ts` ("Turbo/Nx-style") |
73
+ | `'^lint'` | Same task in workspace dependencies | Turbo + Nx | `src/graph/dependency-spec.ts` |
74
+ | `'pkg#lint'` | Arbitrary other package's task | Turbo + Nx | `src/graph/dependency-spec.ts` |
75
+ | `'*' / '^*'` | Wildcard upstream (filter-only) | Turbo's `$TURBO_DEFAULT$`-adjacent | `src/graph/dependency-spec.ts` ("filter-only"), `src/orchestrator/upstream.ts` ("like Turbo/Nx") |
76
+ | `'!<form>'` | Negation (filter-only) | Turbo `inputs` exclusion | `src/graph/dependency-spec.ts` ("filter-only") |
77
+
78
+ Graph construction rejects bare wildcards / negation in `dependsOn`
79
+ itself (`src/graph/task-graph.ts`) — they're filter-only, as the
80
+ parser's docblock marks them (`src/graph/dependency-spec.ts`).
81
+
82
+ ### Filter DSL + selection
83
+
84
+ | Capability | Source | vx source |
85
+ | ------------------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------ |
86
+ | pnpm-style `--filter` (`pkg`, `pkg...`, `...pkg`, path globs) | Turbo + pnpm | `src/workspace/filter.ts` |
87
+ | Transitive-dep expansion (`pkg...`) | Turbo + pnpm | `src/workspace/filter.ts` (the DSL comment) |
88
+ | `[<since>]` git-relative selection | Turbo | `src/workspace/filter.ts` ("Turbo-style"), `src/workspace/affected.ts` ("Matches Turbo's") |
89
+ | `--affected[=<base>]` flag on `vx run` | Turbo + Nx | `src/workspace/affected.ts` |
90
+ | `pkg#task` direct addressing | Turbo + Nx | `src/cli/run.ts` |
91
+
92
+ ### Workspace discovery
93
+
94
+ | Capability | Source | vx source |
95
+ | ----------------------------------------------------- | ------------ | -------------------------------- |
96
+ | Read `pnpm-workspace.yaml` | pnpm + Turbo | `src/workspace/workspace.ts` |
97
+ | Read `package.json` `workspaces` (npm/yarn/bun) | Turbo | `src/workspace/workspace.ts` |
98
+ | Build package-dep graph from workspace `dependencies` | Turbo + Nx | `src/workspace/package-graph.ts` |
99
+ | Lockfile fingerprint at workspace level | Turbo + Nx | `src/workspace/fingerprint.ts` |
100
+
101
+ ### Cache topology
102
+
103
+ | Layer | Turbo | Nx | vx | vx source |
104
+ | ---------------------------------------- | ---------------------------------- | ------------------ | -------------------------------------------------------------- | -------------------------------------------------------------------- |
105
+ | Local: content-addressed artifacts | tarball-per-hash in `.turbo/cache` | `.nx/cache` SQLite | SQLite index + `<hash>.tar.zst`, shared in `~/.vx/<id>/cache/` | `src/cache/cache.ts` |
106
+ | Local: skip-restore when tree is current | yes (fingerprint check) | yes | yes — `isOutputsCurrent` stat check → up-to-date | `src/cache/output-index.ts` |
107
+ | Read-through then write-through layering | yes | yes | yes | `src/cache/layered-cache.ts` |
108
+ | Run-history table for analytics | (no — `--summarize` JSON) | (Nx Cloud) | `runs` + `invocations` tables in `cache.db` | `src/cache/run-history.ts` (tables created in `src/cache/schema.ts`) |
109
+
110
+ ### Remote cache wire
111
+
112
+ The remote cache is **plugin-driven** (owner directive 2026-07-10): core keeps the seams
113
+ (`LayeredCache` + the `RemoteCacheLayer` interface + the `cache`
114
+ plugin capability), and a plugin ships the wire — `@vzn/vx-reapi` speaks
115
+ Bazel's ActionCache + CAS, so any REAPI server works as a remote cache.
116
+ Turbo `/v8/artifacts` compatibility was
117
+ dropped from core — `turboCache()` in `@vzn/vx-migrate` is that wire as a plugin;
118
+ the recipe for any other lives in the plugins guide.
119
+
120
+ ### Tar artifact format
121
+
122
+ Inside the tar we depart from Turbo (we don't mimic Turbo's
123
+ `.turbo/turbo-<task>-<project>.log` file naming — our metadata lives in
124
+ SQLite). But the **on-the-wire framing** stays POSIX-tar so any
125
+ Turbo-aware cache server can transit our blobs unchanged.
126
+
127
+ | Pattern | Source | vx source |
128
+ | ----------------------------------------- | --------------------------------------------- | ---------------------------------------- |
129
+ | POSIX tar framing, in-process pack | Turbo (Rust `tar` crate, `Header::new_gnu()`) | `src/cache/archive.ts` (`tar-stream.ts`) |
130
+ | No AppleDouble `._*` companions | Turbo (in-process writer, no recurse) | `src/cache/archive.ts` (no `tar` spawn) |
131
+ | Entry-name validation on the extract side | (vx-only defense-in-depth) | `src/cache/archive.ts` |
132
+ | zstd compression on the wire | Turbo | `Bun.zstdCompress` (tar.zst artifacts) |
133
+
134
+ ### Scheduler + execution
135
+
136
+ | Pattern | Source | vx source |
137
+ | ------------------------------------------------------------- | -------------------------------------------------- | --------------------------------------------------------------------------- |
138
+ | Topological order, bounded parallelism | Turbo + Nx | `src/graph/scheduler.ts` |
139
+ | Cascade skip: failed task's transitive dependents are skipped | Turbo (mid-mode) + Nx | `src/graph/scheduler.ts` |
140
+ | Independent siblings continue past failure | Turbo `--continue=dependencies-successful` | `src/graph/scheduler.ts` |
141
+ | Persistent / long-running tasks (dev servers) | Turbo `persistent`, Nx `continuous` | `src/exec/runner.ts` (`runPersistent`) + `src/orchestrator/execute-task.ts` |
142
+ | Project-local `node_modules/.bin` on PATH | Turbo + pnpm | `src/exec/env.ts` (`binPaths`) |
143
+ | Implicit project-`package.json` invalidation | Turbo (via lockfile) + Nx (`externalDependencies`) | `src/orchestrator/task-hash.ts` ("Matches Turbo and Nx") |
144
+
145
+ ### Output handling
146
+
147
+ | Pattern | Source | vx source |
148
+ | ------------------------------------------------------------------ | ---------- | ------------------------------------------------ |
149
+ | Glob-based `outputs` declaration | Turbo + Nx | `src/config.ts`, `src/cache/inputs.ts` |
150
+ | Restore by file (archive extract, not symlink/hardlink) | Turbo + Nx | `src/cache/archive.ts` (`extractArtifactStream`) |
151
+ | Log replay on cache hit | Turbo + Nx | `src/orchestrator/hit-restore.ts` |
152
+ | **Wipe outputs before exec AND before restore** (strict ownership) | (vx-only) | `src/cache/inputs.ts` (`cleanOutputs`) |
153
+
154
+ ### CLI conventions
155
+
156
+ | Flag / behavior | Source | vx source |
157
+ | ---------------------------------------------------------------------------------- | -------------- | ----------------------------------- |
158
+ | `--` separator forwards args to task | Turbo | `src/cli/run.ts` |
159
+ | `--filter` DSL (pnpm shape) | Turbo + pnpm | `src/workspace/filter.ts` |
160
+ | `--concurrency <n>` | Turbo | `src/cli/run.ts` |
161
+ | `--no-cache` (all off) / `--force` (reads off, writes on) — distinct, not synonyms | Turbo-adjacent | `src/cli/run.ts` |
162
+ | `--dry` / `--dry=json` (plan output) | Turbo | `src/orchestrator/plan.ts` |
163
+ | `--graph[=<path>]` (DOT) | Turbo + Nx | `src/cli/plan-format.ts` |
164
+ | `--summarize[=<path>]` (per-run JSON) | Turbo | `src/orchestrator/run-artifacts.ts` |
165
+ | `--profile[=<path>]` (Chrome-trace) | Turbo | `src/orchestrator/run-artifacts.ts` |
166
+ | `--affected[=<base>]` | Turbo + Nx | `src/workspace/affected.ts` |
167
+ | `watch <task>` subcommand | Turbo + Nx | `src/cli/watch.ts` |
168
+
169
+ ### Output presentation
170
+
171
+ | Pattern | Source | vx source |
172
+ | ---------------------------------------------------- | ------------------------------------------------ | --------------------------------------------------- |
173
+ | Framed per-task blocks (`┌─ … └─`) | Turbo | `src/orchestrator/framed-output.ts` |
174
+ | End-of-run summary footer (task/cache meters + time) | Turbo-adjacent (vx renders meters, not a banner) | `src/orchestrator/summary.ts` |
175
+ | Per-task output buffered until task finishes | Turbo | `src/orchestrator/framed-output.ts` ("Turbo-style") |
176
+
177
+ (There is no `>>> FULL TURBO`-style all-cached banner — a fully-green
178
+ cache meter carries the message.)
179
+
180
+ ## Performance
181
+
182
+ Sharing the patterns doesn't mean sharing the overhead. On the
183
+ 476-package / 1,428-node synthetic workspace
184
+ (`packages/vx-bench/compare.ts 20 25 1`, 2026-09-02):
185
+
186
+ | Runner | Fresh (cold) | Warm (no restore) | Warm (restore) |
187
+ | ------ | --------------------- | ----------------------- | ----------------------- |
188
+ | **vx** | 1m 40s | **297 ms** | **416 ms** |
189
+ | Turbo | 1m 40s (vx same) | 342 ms (vx 15% faster) | 612 ms (vx 47% faster) |
190
+ | Nx | 3m 23s (vx 2× faster) | 1.38 s (vx 4.6× faster) | 1.33 s (vx 3.2× faster) |
191
+
192
+ vx N% faster: that tool takes N% longer than vx; N× faster: N times as long.
193
+
194
+ Full breakdown + methodology in [`benchmarks.md`](./benchmarks.md).
195
+
196
+ ## Where the lineage diverges
197
+
198
+ For features Turbo or Nx have that vx **lacks**, see
199
+ [`comparison.md` § Gaps](./comparison.md#gaps-for-vznvx-the-running-list).
200
+
201
+ For places vx made a deliberately different call (TypeScript config,
202
+ resolved-config hash, strict output ownership, no daemon, no TUI), see
203
+ [`comparison.md` § Where vx is ahead](./comparison.md#where-vx-is-ahead)
204
+ and [`README.md`](./README.md#the-problems).
205
+
206
+ ## Quick citation index
207
+
208
+ Every "Turbo" / "Nx parity" claim above is anchored in a comment in
209
+ the vx source. To audit:
210
+
211
+ ```sh
212
+ grep -rn "Turbo\|Nx parity\|turborepo" src/
213
+ ```
214
+
215
+ Each line is a deliberate decision to mirror an upstream pattern, and
216
+ every phrase this page quotes beside a source path is held to that
217
+ file by `tests/patterns-doc-drift.test.ts`. Adding to that list is preferable to inventing
218
+ new vocabulary — vx's value is in the swaps, not in renaming the
219
+ parts we kept.