run.rules.dependencies tags the layers of a monorepo and says which may reach which. vx lock refuses a dependency that breaks a rule and names every broken edge. Runs never check it, so they pay nothing. nx() carries most Nx module-boundary constraints over.
config
config
A UI kit that imports a checkout flow, or an app that reaches into a
helper it should not see, is easy to add and hard to remove. Tag the
layers, write the rules once, and vx lock holds every change to them.
flowchart TD
web["@demo/web<br/>type:app"] --> checkout["@demo/checkout<br/>type:feature"]
web --> button["@demo/button<br/>type:ui"]
web -. not allowed .-> utils["@demo/utils<br/>type:util"]
button -. denied .-> checkout
style utils stroke:#ff5e9c,stroke-width:2px
style button stroke:#ff5e9c,stroke-width:2px
A dependency must match one of allow, when it is given, and none of
deny. Every rule that selects a project applies, so a deny wins over
any allow. A project no rule selects is free.
The rules are checked against each project’s direct workspace
dependencies: its package.json and its cross-project dependsOn.
vx lock refuses a graph that breaks one, names every broken edge, and
writes nothing:
vx lock --check in CI fails the same way, so a pull request that adds
a forbidden dependency goes red. Runs never check these rules: under CI
they load configs from the lock, which already passed them
(Lock once, run frozen).
nx() from @vzn/vx-migrate reads the depConstraints of
@nx/enforce-module-boundaries in the root ESLint config and fills
run.rules.dependencies when the workspace sets none. It reads the file
and never runs it, so ESLint’s plugins do not load on a vx run.
One difference: Nx’s @nx/enforce-module-boundarieslint rule
checks source imports, while vx checks the dependencies a project
declares. allSourceTags, /regex/ tags and constraints that only ban external
imports are left out; the
vx-migrate README
lists them. The schema is in the
configuration reference.
Every vx setting has one path in .vx/workspace.ts, and its environment variable and flag are spelled from it. run.retries is VX_RUN_RETRIES and --retries. Learn one name and you know all three. A duration carries its unit, and vx info says where each value came from.
A sandbox refusal names the path and the grant to add. Two tasks writing the same folder are stopped before they delete each other. And a config that hangs times out with its name.
Edges in dependsOn, selections in --filter. A small grammar covers upstream builds, cross-project tasks, groups, dependents, folders and tags. And a name that matches nothing stops the run before it starts.
Turborepo, Nx and other product names are trademarks of their owners. vx is not affiliated with or endorsed by them.