AGENTS.md@.moon · git:20261020.8d47d7a · 2026-10-20 · sha256 4d3631284e0d673b
AGENTS.md@.moon git:20261020.8d47d7aA
Immutable. This exact content never changes and is served at /api/v1/blob/4d3631284e0d673b.
# Moon configuration
How tasks, inputs and dependencies are laid out across the workspace. Read
this before editing any `moon.yml` or a file under `.moon/`.
## Turborepo runs beside moon
Every task also exists in a `turbo.json`
([turborepo.md](../docs/design/build/turborepo.md)). Until the cutover in
[turborepo-migration.md](../docs/design/build/turborepo-migration.md), a
change to a task's command, dependencies, inputs or outputs goes into both
files. Moon is the runner of record; CI runs the turbo version as the
non-required `ci-turbo` job.
Where the two differ on purpose:
- Moon's `test` runs each package's `test:all` script, turbo runs `test`. In
the browser-test packages, `test` wraps `scripts/task-guard.mjs` for
turbo's retries and `test:all` calls Vitest directly, because moon has its
own `retryCount`.
- The Go `format` and `lint` tasks keep inline scripts here; turbo runs copies
of them (`scripts/go-format-check.mjs`, `scripts/go-lint.mjs`). Change both.
- `scripts/task-guard.mjs` in api `generate` and config `sync-schemas` acts
only under turbo; under moon it runs the command once, unchanged.
- Some scripts set values a moon task's `env` sets too, because turbo cannot.
They set them as defaults (`${VAR-default}`), so a caller's value still
wins.
- Turbo's console `build` lists the git-ignored `.env*` files as inputs. Moon
hashes only the files git sees, listed or not, so `moon.yml` does not list
them: under moon a changed `.env.local` still restores the old build.
## Shared tasks live in `.moon/tasks`
- `all.yml` applies to every project. Its `implicitInputs` (the root files
that fix the toolchain and the installed dependencies, plus
`.moon/tasks/**/*.yml`) re-run every task when they change.
- `typescript.yml` defines `lint`, `typecheck`, `build`
(`outputs: dist/**/*`) and `test` once for every TypeScript project.
`javascript.yml` gives the JavaScript projects the same `lint`.
- A project's `moon.yml` never repeats a shared task's command. It adds only
`deps` (appended), `outputs` with `options.mergeOutputs: "replace"`, a
different command with `options.mergeArgs: "replace"`, or options (a mutex,
retries, `runInCI`).
- A project with nothing to build or test excludes the task with
`workspace.inheritedTasks.exclude` and a comment saying why, never with an
empty script.
## Inputs
A task uses moon's default inputs, the whole project folder, so a new file is
covered without a list to maintain. Never write an `inputs` list naming a
package's own files. The exceptions name only what lies outside the folder:
- A task that reads files outside its project lists `**/*` plus that path
(`api:generate` reads `/api/openapi/**/*.yaml`).
- `server` keeps its file groups because its code lives at the repo root, and
`bench` because `tools/bench` compiles the root module through
`replace github.com/zitadel/nextgen => ../..` (`tools/bench/go.mod`).
Anything compiled in through `//go:embed` or read by a test belongs in them.
- The root `workspace` project's `test` and `check-*` tasks list their files,
because its folder is the whole repo.
## Dependencies between projects
- A task that uses another package depends on that package's task, and the
project declares the package in `package.json` (`devDependencies` when only
tests or tooling use it).
- Use another package through its exports, never a relative path into its
folder.
- Never list a dependency another one already reaches, unless the path runs
through a task with no `outputs`, which moon does not hash (`server:test`
lists the UI builds although `server:vet` already orders them).
- Give a depended-on task its real `outputs`: without them, a change there
does not re-run its dependents.
- An edge on what another project builds (embedded UIs, built apps and
binaries, the release tasks) has no `package.json` entry; the graph check's
allowlists hold them. `release:build-public-packages` names every public
package, because `release:check-graph` requires it.
## Check the graph
Run it after changing any `moon.yml`, `.moon/` file or workspace dependency;
CI runs it too:
```sh
moon run workspace:check-moon-graph
```
It fails on a transitive duplicate reached through hashed edges, a
cross-project dependency without a matching `package.json` entry, or a
declared workspace package no task reaches. Extend its allowlists
(`scripts/check-moon-graph.mjs`) only for the same kind of case, with a
comment saying why.