Work with Claude Code
You don’t need to be a platform engineer to write motions. If you can describe the routine, read a diff, and run three commands, Claude Code, or any coding assistant, can do most of the typing. Motions is well suited to it: a motion is small, its rules are strict, and check and test tell the assistant quickly when it’s wrong.
Set up the repository
Section titled “Set up the repository”Add a CLAUDE.md at the root of the tenant repository. Claude Code reads it at the start of every session. This one tells it the layout, the rules that aren’t obvious from the code, and what “done” means:
# Órbita's motions
This repository is a Motions tenant. Motions runs these motions over batches of companies from our CRM; people answer the questions they ask and approve the CRM changes they propose. Docs: https://docs.pruma.tech/docs/
## Layout
- `tenant.yaml`, `crm.yaml`: the tenant and its CRM field map. Don't change `system` or `mode` in `crm.yaml` without asking.- `motions/<id>/motion.yaml`: the steps and who decides. Reference: https://docs.pruma.tech/docs/reference/motion-yaml/- `motions/<id>/steps/*.ts`: one file per step, `export default defineStep({ async run(company, ctx) { … } })` from `@motionskit/sdk`.- `motions/<id>/rules.yaml`: values the steps read as `ctx.rules`. Put thresholds and lists here, not in code.- `motions/<id>/tests/*.test.ts`: Vitest tests using `runStep` from `@motionskit/sdk/testing`.- `fixtures/sources/*.json`: recorded source answers for tests and fixture mode.- `sources/<id>/index.ts`: custom sources, `defineSource` from `@motionskit/sdk`.
## Rules for steps
- No `fetch`, no Node built-ins. Data from outside the CRM comes only through `ctx.sources`, and every operation a step calls must be listed under its `sources` in `motion.yaml`.- CRM reads need the object in the step's `crm.read`; writes are proposals (`ctx.crm.*.create/update`, `associate`, `notes.*`) that need the action in `crm.propose` and a `changeset` step after them.- A step returns `{ facts }`, `drop(reason)`, `hold(reason)`, or nothing. Reasons must be in the motion's `reasons`.- When a case is unclear, ask with `ctx.ask({ type, prompt, options })` instead of guessing: 2 to 4 options, each a full sentence; options that end the company's run have `effect: "hold"` or `"drop"`. Every question type needs a queue in `motion.yaml`.- Steps re-run from the top after an answer, a retry or a wait. Keep them deterministic: use `ctx.now()`, never the wall clock.- Write prompts and options in the language of the people who answer them.
## Before saying something is done
1. `npx motions check` passes.2. `npm run typecheck` passes.3. `npx motions test` passes, with a test for each new path: each drop, each question, and the answered case.4. New source calls have fixtures in `fixtures/sources/` for the tests.
Never push, never run `motions push`, and never put tokens or keys in any file. Open a pull request; a person reviews and merges it.Adjust the language rule and anything specific to your client. Keep it short: it’s read every time.
Describe the routine, not the code
Section titled “Describe the routine, not the code”The best requests read like a brief to a colleague. Say what the step decides, from what, and what a person should be asked:
Add a step after
identitycalledfit. Using the CNPJ facts and the company’s segment field, drop companies with fewer thanmin_employeesemployees (put the number in rules.yaml, start at 50) with reasontoo_small. If the segment is empty, ask the account owner which segment it is, with the four segments in rules.yaml as options. Add tests for each case, with fixtures.
- Start from the motion. Ask it to read
motion.yamland the steps it’ll touch, and to say what it plans before writing. - One step at a time. A step and its tests per request. Review, run, commit, then the next.
- Make it run the checks.
npx motions check,npm run typecheck,npx motions test. ItsCLAUDE.mdsays so, but ask for the output. - Review the parts that matter. You don’t need to read every line. Read:
- the
motion.yamldiff: sources,crm.propose, caps, queues and owners; - each question’s prompt and options, as the person answering will see them;
- the tests’ names, which should read like the cases you described.
- the
- Try it on a draft. Push, open a batch on the draft with Try on a batch on a few companies, and look at the facts and questions in the drawer.
Point it at the docs
Section titled “Point it at the docs”These pages are the reference. When the assistant is unsure about an API, give it the page:
- motion.yaml reference: every key, with its default.
- SDK reference:
defineStep,ctx, the CRM API and the testing helpers. - Sources catalog: every built-in operation and its price.
- Errors: what each error message means and how to fix it.
When it gets stuck
Section titled “When it gets stuck”- “add it to the step’s sources”: it called an operation the step doesn’t list. The fix is in
motion.yaml, not the step. - “no fixture matches”: a test called a source with a request no fixture covers. Ask it to add a fixture with the exact request from the message.
- A test passes but the batch fails: compare the fixture’s
datawith what the provider really returns, using the operation’s type in the SDK. An assistant that invents fixtures can invent fields. - It wants to work around a rule: steps can’t fetch, can’t write to the CRM directly, and re-run from the top. Those are the platform’s guarantees; a design that fights them is the wrong design.
Motions is built by Pruma. These docs describe what the platform does today; Limitations lists what it doesn't do yet. Open the app