Skip to content
GitHubRSS

Turbo / Nx parity map

What a Turborepo or Nx user relies on, the vx spelling of it, and the test that proves it. Two end-to-end suites run the real CLI on one four-package fixture (tests/helpers/parity.ts: app → ui → lib, app → lib, docs alone) and name each case for the upstream contract: tests/parity-turbo.test.ts (turborepo.dev, 2.10) and tests/parity-nx.test.ts (nx.dev, 23). They are the scan surface; the right column names the suite that pins the behaviour in depth. A deliberate divergence is marked ≠ and explained.

TurbovxDeep pin
"dependsOn": ["^build"]dependsOn: ['^build']tests/task-graph.test.ts
"dependsOn": ["build"] (same package)dependsOn: ['build']tests/task-graph.test.ts
"dependsOn": ["pkg#task"]dependsOn: ['pkg#task']tests/dependency-spec.test.ts
--only--exclude-dependencies[=<names>]tests/cli.test.ts
--dry, --dry=json--dry, --dry=jsontests/plan-format.test.ts, tests/plan-predict.test.ts
--graph=file.dot--graph[=<path>] (DOT; edges point dependency → dependent)tests/cli.test.ts
--filter=pkg, pkg..., ...pkg, ./dir, !pkgthe same DSL (docs/cli.md § Filter DSL)tests/filter.test.ts
--filter=[ref], --affected--filter '[<ref>]', --affected[=<ref>]; ...[<ref>] adds dependentstests/affected.test.ts
--filter + --affected intersect≠ they union (docs/comparison.md § Filter DSL)tests/filter.test.ts
a filter matching nothing exits 1the sametests/task-selection.test.ts
second run hits; missing outputs restored; logs replayedthe same, with outputs cleaned before exec and restore (≠ additive)tests/orchestrator.test.ts, tests/output-dirs.test.ts
"inputs": [...] narrows the hashcache.inputs.files (required; nothing is inferred)tests/inputs.test.ts, tests/task-hash.test.ts
"inputs": ["src/"], "outputs": ["dist"] (a directory literal)the same: a literal is the file or its whole treetests/inputs-resolution.test.ts
--filter=./apps/* (a path glob)--filter './apps/*', {apps/**} — over root-relative project dirstests/filter.test.ts
--affected diffs from the merge basethe sametests/affected.test.ts
TURBO_CACHE=remote:rw with no remote → “Remote caching disabled”--cache=remote:rw with no remote layer → one status linetests/parity-turbo.test.ts
a dependency’s change re-hashes dependentsupstream input keys are folded (never outputs)tests/task-hash-derive.test.ts, tests/upstream.test.ts
"env": ["MODE"]cache.inputs.env: ['MODE']tests/env.test.ts
"passThroughEnv"exec.env.passThrough (reaches the command, never the hash)tests/env.test.ts
"cache": falseno cache block (noCache: true in --summarize)tests/no-cache-word.test.ts, tests/run-artifacts.test.ts
--force--force (reads off, writes on)tests/cli.test.ts
--cache=local:r,remote: / --no-cache--cache=<spec> / --no-cachetests/cli.test.ts, tests/download-policy.test.ts
turbo run test -- --watchvx run test -- --watch (forwarded args are in the hash)tests/cli.test.ts
the global hash covers the lockfilethe workspace fingerprint, in every key; @vzn/vx-lockfile narrows ittests/fingerprint.test.ts, tests/lockfile-claim.test.ts
a package’s package.json is in its hashthe sametests/task-hash.test.ts
--continue=dependencies-successful (default)--continue=deps-ok (default)tests/continue-taint.test.ts
--continue=always--continue (a task built on a failure runs but is never saved)tests/continue-taint.test.ts
--continue=never--continue=nevertests/scheduler.test.ts
graceful shutdown (graceful_shutdown_test.rs)the signal received, VX_KILL_GRACE_MS, SIGKILL; a second one skips the gracetests/signal-handling.test.ts
recursive turbo run refused (recursive_turbo_test.rs)VX_RUN_WORKSPACE marker; vx run in its own workspace is refusedtests/recursive-run.test.ts
failures are never cachedthe sametests/execute-task.test.ts
--output-logs=errors-only, hash-only, nonethe same (full too; the default follows the run’s flow)tests/output-flow.test.ts
--summarize--summarize[=<path>]tests/run-artifacts.test.ts
--profile--profile[=<path>]tests/run-artifacts.test.ts
--concurrency--concurrency <n | n%>tests/scheduler.test.ts
"persistent": trueexec.persistent: { readyWhen } (readiness gates dependents)tests/persistent.test.ts, tests/dev.test.ts
turbo watchvx watch <task>tests/watch-rules.test.ts, tests/watch-loop.test.ts, tests/watch-signals.test.ts
turbo prune≠ removed 2026-09-11 (was @vzn/vx-prune): copy the workspace and --filter the build
--summarize + Vercel Remote CacheturboCache() in @vzn/vx-migrate (same wire, self-hosted or Vercel)packages/vx-migrate/tests/turbo-cache.test.ts
$TURBO_ROOT$/tsconfig.json inputcache.inputs.workspaceFiles: ['tsconfig.json'] (outputs too)tests/workspace-files.test.ts
globalDependencies, globalEnv, extends≠ rejected: configs are TypeScript and compose by importdocs/comparison.md § Likely-worth-adding item 4 (global inputs and env), § Config schema comparison “Target defaults / inheritance” (extends)
NxvxDeep pin
nx run app:buildvx run app#buildtests/task-selection.test.ts
nx run app:build:productionvx run app#build:production — under nx() or the migration a configuration is a task of its own; the default configuration is the base taskpackages/vx-migrate/tests/nx.test.ts
nx run-many -t build testvx run build test --alltests/task-selection.test.ts
nx run-many -t lint -p app uivx run lint --filter app --filter uitests/filter.test.ts
--exclude docs--filter '!docs'tests/filter.test.ts
nx affected (changed and dependents)the same: --affected is changed and dependents; --filter '[<ref>]' is the changed settests/affected-dependents.test.ts
nx affected --base=main--affected=maintests/affected.test.ts
packages: ['packages/*', '!packages/fixtures'] (a negated workspace glob)the same: negations subtract in discoverytests/workspace.test.ts
nx affected with nothing changed exits 0the same (nothing affected since <ref>)tests/affected.test.ts
--parallel=3--concurrency 3tests/scheduler.test.ts
--skipNxCache--no-cachetests/cli.test.ts
--nxBail--continue=nevertests/scheduler.test.ts
nx show projects, nx show project app --jsonvx show, vx show app --format jsontests/show-info.test.ts
nx graphvx run <task> --graph (DOT)tests/cli.test.ts
"dependsOn": ["^build"] / { projects: "dependencies" }dependsOn: ['^build']tests/task-graph.test.ts
{ projects: ["ui"], target: "build" }dependsOn: ['ui#build']tests/dependency-spec.test.ts
"dependsOn": ["build-*"] (wildcards)dependsOn: ['check.*'], ['^build.*']tests/wildcard-depends.test.ts
a target that only aggregatesa task with no exec — a group; its members run, it is not a tasktests/task-graph.test.ts, tests/tally.test.ts
{ "runtime": "node -v" } inputcache.inputs.runtime: ['node -v'] (workspaceRuntime at the root)tests/runtime-inputs.test.ts
{ "env": "NODE_ENV" } inputcache.inputs.env: ['NODE_ENV']tests/env.test.ts
{workspaceRoot}/tsconfig.base.json inputcache.inputs.workspaceFiles: ['tsconfig.base.json']tests/workspace-files.test.ts
"outputs" restored on a hitcache.outputs.files (cleaned before exec and restore, ≠ additive)tests/orchestrator.test.ts
project.json / package.json changes re-hashthe resolved config and the project package.json are in every keytests/config-staleness.test.ts, tests/task-hash.test.ts
failures are never cachedthe sametests/execute-task.test.ts
"continuous": trueexec.persistent: { readyWhen }tests/persistent.test.ts
nx watchvx watch <task>tests/watch-rules.test.ts, tests/watch-loop.test.ts
Nx Cloud’s flaky-task flaglocal: same key, both outcomes → the footer, --summarize flaky, vx infotests/flaky.test.ts, tests/failure-mode.test.ts
lockfile-aware affected / hashing (the daemon’s pruned lockfile)@vzn/vx-lockfile: per-project dependency closures, --affected followspackages/vx-lockfile/tests, tests/lockfile-claim.test.ts
nx resetvx cache prunetests/cache.test.ts, tests/cli.test.ts
namedInputs, targetDefaults, {projectRoot} tokens, .env auto-loading, tags≠ rejected: TypeScript configs compose by importdocs/comparison.md § Explicitly rejected (namedInputs, targetDefaults), § Deliberate divergences › Config schema (tokens), › Env handling (.env), › Task graph (tags)
Nx Cloud (agents, dashboard, DTE)≠ not in this repo: the executor / cache / telemetry seams, @vzn/vx-reapipackages/vx-reapi/tests
  • Every row’s vx spelling is documented in docs/cli.md or docs/schema.md; the parity suite asserts the row against the real CLI, the deep pin asserts the edge cases.
  • The two ≠ rows that change what a command selects or leaves on disk are decisions recorded in docs/comparison.md with the reasoning: union not intersection under § Deliberate divergences, cleaned-not-additive outputs under § Where vx is ahead.
  • What neither runner does and vx does — the resolved-config hash, vx lock + vx run --frozen, vx why, restore-ahead scheduling — is docs/comparison.md § Where vx is ahead; the parity map lists only what a user of the other runner would look for.