Working on a ticket
Follow one piece of work through a repository with components.
This page follows one ticket through Acme's repository, from the moment it starts to the pull request, and shows what dmx does with the components at each step. It assumes the components are set up as in Setting up components.
As a reminder, Acme has three components: ledger, payments and billing-ui. billing-ui uses payments, and payments uses ledger.
The ticket
The payments team has a bug: refund fees are rounded the wrong way. A developer on the team starts the work as usual:
/dmx/run-loop spec Fix the rounding of refund feesNothing about running loops changes in a repository with components. The developer answers the same questions at the same points. The differences are in what dmx reads, runs and writes along the way.
Deciding the ticket's components
First, dmx works out which components the ticket is about. As it writes the spec, it records them in the spec's frontmatter:
---
ticket: GH-412
branch: feature-gh-412-refund-fees
components: [payments]
---If the request doesn't make the components clear, it asks.
The list can grow as the work goes on. If the fix turns out to need a change in services/ledger/, ledger joins the list as soon as the branch changes one of its files. To change the list yourself, tell the agent, for example "this also affects billing-ui".
Every message from the loops starts with the current list, so you always know what dmx thinks the ticket is about:
Components: payments (.dmx/components/payments/)What the agent reads
The memory bank has two levels: the repository-wide memory bank at the top of .dmx/, and each component's memory in its folder. For the refund-fees ticket, the agent reads:
- the repository-wide memory bank, in
.dmx/; - payments' memory, in
.dmx/components/payments/; - ledger's memory, in
.dmx/components/ledger/, because payments depends on ledger and its code calls ledger's API.
It doesn't read billing-ui's memory. billing-ui depends on payments, not the other way round, so how billing-ui works doesn't matter for a fix inside payments.
Dependencies are followed one step. A billing-ui ticket reads payments' memory, but not ledger's.
What runs before the work moves on
At the end of the Dev loop, and again in the Validate loop, dmx checks the work before it moves on: it runs tests, and any other checks the loop includes. Each of these stopping points is a gate.
Tests
At each gate, dmx runs the tests of every component the branch changed, and of every component that depends on one of them. For the refund-fees fix, that's payments and billing-ui:
Tests passed — 2 runs
✓ payments — go test ./... in services/payments (exit 0)
✓ billing-ui — pnpm --filter billing-ui test (exit 0)Tests explains this in full, including what to do about tests that fail whatever the change.
Checks a component adds
A team can add checks of its own to a component. They run at the gates of every ticket that includes the component. The ledger team, for example, wants to make sure no code stores money as a floating-point number. It adds a validator to the Validate loop for its component:
# .dmx/components/ledger/component.yaml
validators:
validate: [no_float_money]no_float_money is a script in .dmx/components/ledger/validators/. A validator is a small program that checks the work and reports pass or fail.
The refund-fees ticket doesn't include ledger, so this check doesn't run. If the fix had touched ledger, it would. A failure names the component, for example ledger/no_floats, so it's clear whose rule was broken. For the full set of options, see the component.yaml reference.
Where learnings go
While it works, the agent writes down what it learns in the ticket's learning inbox, activeContext.md. Each learning is tagged with what it's about:
## Open Learnings
- [payments] Refund fees are rounded half-up before they're posted.
- [ledger] Refunds post a reversing entry, never a negative amount.
- [repo-wide] CI caches Go modules per service.When the pull request opens, the payments learning goes into payments' memory. The ticket changed payments' code, so the payments team, reviewing the pull request, is the right team to check it.
The other two wait in the ticket's folder. Ledger's memory belongs to the ledger team, and the repository-wide memory bank to the platform team. Neither reviews this pull request.
The ledger learning moves into ledger's memory the next time a pull request changes ledger's code, so the ledger team reviews it there. To move waiting learnings sooner, run /dmx/update-memory ledger. /dmx/status and dmx check show how many learnings are waiting, and for whom.
Where the ticket is filed
Teams in a monorepo often track their work in different places. At Acme, each team has its own Jira project: the ledger team uses LEDG, the payments team PAY, and the billing team BILL.
First, tell dmx which Jira projects this repository's tickets can be filed in. Add their project keys to .dmx/config.md, on one line:
jira_projects: [LEDG, PAY, BILL]dmx won't file a ticket in a project that isn't on this list. The project you chose when you set up dmx, project_key in the same file, stays the default for work that isn't about one component. For GitHub Issues, list repositories instead, such as github_issue_repos: [acme/mono, acme/ledger-issues].
Next, set each component's project in its component.yaml:
# .dmx/components/payments/component.yaml
ticket: {jira_project: PAY}When dmx files the refund-fees ticket, it picks the destination in this order:
- the project the developer named, if any;
- otherwise, the project of the ticket's components, here
PAY; - otherwise, the default
project_key.
When dmx picks the project itself, it asks before filing: "Create the ticket in PAY (default for payments)?" If a ticket's components have different projects, it lists them and asks which to use.
Component skills and loops
A team can give its component skills and loops of its own. The ledger team, for example, has a loop that backfills historical balances. It lives in .dmx/components/ledger/loops/backfill.yaml, and its name includes the component's:
/dmx/run-loop ledger/backfillA component's skills and loops are available only on tickets that include the component, and they're listed first on those tickets.
A component's loop can run the component's own validators. Name one in the loop by its file name, such as tool: balance_check for .dmx/components/ledger/validators/balance_check.py, and dmx runs the component's copy. Its checks are reported with the component's id, such as ledger/balanced. A validator the component doesn't have, such as run_tests, runs from the repository as usual.
To have the agent write a skill, loop or validator, ask it to. It follows /dmx/extend, which covers where each file goes and the format of each.
They add to the repository's loops; they don't replace them. Every ticket runs the same spec, plan, dev, validate and release loops, whichever components it includes. That way a ticket that spans payments and ledger never has to choose between two versions of dev. To change how a standard loop works for everyone, change the repository's own copy. To add steps for one component, give it its own loop, or its own checks.
A component can also have its own shared sources, so its team can update the skills and validators it shares with other repositories without the platform team's approval.