dmx

Setting up components

Declare the components of a monorepo, step by step.

This page walks through declaring the components of a repository. It uses Acme's repository from Monorepos: three components, ledger, payments and billing-ui, where billing-ui uses payments and payments uses ledger. Read that page first if components, paths and dependencies are new to you.

By the end, each component has a component.yaml with the right paths and dependencies. Each component also has a working test command, its own memory, and an owning team, and the setup is committed.

Before you start:

  • dmx is already set up in the repository: you've run /dmx/init once.
  • You're on the integration branch, the branch feature work merges into. It's branch_base in .dmx/config.md, often main.

Step 1: Create the components

If your repository uses a workspace tool to manage its packages, dmx can create the components for you. Run /dmx/init again. In a repository that's already set up, it updates the configuration and leaves your memory bank alone. It looks for packages listed in:

  • pnpm-workspace.yaml (pnpm);
  • the workspaces field of package.json (npm or yarn);
  • go.work (Go);
  • [tool.uv.workspace] in pyproject.toml (uv);
  • [workspace] in Cargo.toml (Cargo).

When it finds two or more packages, it lists them and asks which should become components. At Acme, go.work lists the two Go services and pnpm-workspace.yaml lists the web app:

This repo is a workspace. dmx can treat each package as a component:

  billing-ui   web/billing-ui      Billing web app
  ledger       services/ledger     Ledger service, records account balances
  payments     services/payments   Payment processing service

Create these components? (yes / no / list the ids to keep or rename)

To keep all three, answer yes. To keep some, or rename one, list the ids you want.

For a folder that no workspace file lists, add it by its path:

/dmx/add-component services/fraud

Either way, dmx writes .dmx/components/<id>/component.yaml for each component, with a description and paths. Check the description reads well; you can edit it any time.

Step 2: Check what each component owns

dmx sets each component's paths to the package's folder, such as services/ledger/**. Read through each component.yaml and add anything else that belongs to the component.

The most common addition is tests stored outside the package. Some repositories keep all their tests in a top-level tests/ folder, with one folder per package. In that case, add the component's test folder to its paths:

paths: ["packages/ledger/**", "tests/ledger/**"]

That way, changing those tests counts as changing the component, and dmx can find them when it runs the component's tests.

Files the whole repository shares, such as Acme's Makefile, go.work and CI workflows, should stay in no component. They're repo-wide files.

Step 3: Declare dependencies

For each component that uses another, add depends_on to its component.yaml:

# .dmx/components/payments/component.yaml
depends_on: [ledger]
# .dmx/components/billing-ui/component.yaml
depends_on: [payments]

To find dependencies, look at what each package imports or calls. Package manifests are a good start: a require in go.mod, a dependency on another workspace package in package.json, or a dependency on another member in pyproject.toml. Calls between services, such as payments calling ledger's API, don't always show up in manifests, so add those too.

The agent can suggest dependencies from the manifests, but check what it suggests. dmx only uses the dependencies you write down.

Step 4: Give each component a test command

dmx runs a component's tests with one command. For Go, Node, Python and Rust code it works out a command by itself. At Acme, ledger and payments get go test commands and billing-ui gets its pnpm test script.

Check what dmx will run with dmx check (more on it in Step 7). On the integration branch, and before you set test_command below, it lists each component's command under Runs. It also reports any component it has no command for.

When a command isn't right, set your own with test in the component's component.yaml:

test:
  cmd: make test
  cwd: services/payments

cmd is the command, and cwd is the folder to run it in, relative to the root of the repository.

Look closely at how your CI runs the same tests. If the CI job sets environment variables or flags, such as CI=true or a setting that turns off a slow check, put them in the command too, or a test that passes in CI can fail here. When the agent writes a test command for you during /dmx/init, it reads your CI workflow, copies those settings, and tells you which it took.

Last, tell dmx how to test a change to a repo-wide file, with test_command in .dmx/config.md:

test_command: make test

Without it, a repo-wide change runs every component's tests. Tests covers how dmx picks what to run, and what to do about tests that fail for reasons unrelated to the change.

Step 5: Give each component its own memory

Your memory bank was written before the components existed, so it describes the whole repository. Some of what it records is really about one component, such as how ledger rounds amounts.

Moving that content into the component's memory, in its folder, means dmx reads it only when the work involves that component. /dmx/init and /dmx/add-component offer to do it. They show each move as a diff, such as from .dmx/systemPatterns.md to .dmx/components/ledger/systemPatterns.md, and apply only what you accept. Content about more than one component stays in the repository-wide memory bank.

Step 6: Assign owners

Give each team its component's folder in your repository's CODEOWNERS file. dmx suggests these lines:

# .github/CODEOWNERS
/.dmx/                          @acme/platform
/.dmx/components/ledger/        @acme/ledger
/.dmx/components/payments/      @acme/payments
/.dmx/components/billing-ui/    @acme/billing
/.dmx/jobs/
  • .dmx/ belongs to whoever looks after dmx for the repository, here the platform team.
  • Each component folder belongs to that component's team, so a change to ledger's memory goes to the ledger team for review.
  • .dmx/jobs/ has no owner. It holds each piece of work's own working files, which shouldn't need anyone's approval.

Step 7: Check the setup

Component settings live in .dmx/, away from the code they describe. A misspelled dependency or a folder that's since moved would otherwise go unnoticed. Run dmx check, or /dmx/check in chat, to find these mistakes:

$ dmx check
Setup
  ✓ 3 components
  ✗ payments: depends_on "ledgr" is not a component (fix depends_on in .dmx/components/payments/component.yaml)
  ! services/fraud has no component (run /dmx/add-component services/fraud)

Each line ends with how to fix it. Fix every ✗. A ! is worth a look, but some are deliberate; here, perhaps services/fraud isn't worth a component of its own yet.

dmx check never changes a file, and exits with an error when it finds a ✗, so you can also run it in CI.

Step 8: Commit the setup

dmx starts each new piece of work from the integration branch, so the setup needs to be there. At the end of /dmx/init, choose either to commit it to the integration branch directly, or to open a pull request for it and merge that first.

What's next

The components are in place. Working on a ticket shows how dmx uses them from the next piece of work onwards.

When a team needs more, a component can also have:

On this page