Skip to main content
Miles is open source under the LICENSE file in the repo. Contributions of every size are welcome: bug reports, doc fixes, new model recipes, full features.

Repository layout

The local loop

Code style

Formatting is not a matter of taste here, it is a hook. .pre-commit-config.yaml is the enforcement, the pre-commit workflow runs pre-commit run --all-files on every PR, and you can reproduce it exactly with the same command locally. Three hooks are Miles-specific bans, each pointing at the API you should use instead: If a commit legitimately needs an exception, the hooks carry exclude patterns; extend those in the same PR rather than disabling a hook. Beyond formatting, the conventions a reviewer will hold you to live in .claude/rules/general-code-style.md: prefer stateless and immutable, keep functions under roughly 100 lines and files under roughly 1000, initialize derived values once, keep imports at the top, use absolute imports, prefer keyword arguments where they add clarity. It applies to miles/**/*.py, scripts/**/*.py, tools/**/*.py, train.py and train_async.py.

What lives in .claude

The .claude directory is how the repo hands its conventions to coding agents, and it is worth reading even if you never run one, because it is where several rules are written down exactly once. .claude/rules/ holds path-scoped conventions. Each file carries a paths: front matter list, and the rule applies to any file matching it. general-code-style.md is the one described above. AGENTS.md at the repo root points Codex at the same file, so both agents and humans review against one document. .claude/skills/ holds procedures, one directory per skill with a SKILL.md. They are workflows rather than style rules:

The doc-dev sentinel

Some files are bound to a document. A # doc-dev: line in a file’s own comment syntax is the opt-in: bare, it binds the file’s own header block; with a repo-relative path, it also binds that central document. Editing such a file means updating its documentation in the same change, and editing the document means finding the files that name it.
Current sentinels, so you know when you have walked into one: Grep for doc-dev: before editing anything under .github/workflows/ or docker/. A change that lands the code and leaves the document stale is the failure mode this convention exists to prevent.

Running CI

What a PR runs

Two things start automatically on every PR: the pre-commit workflow, and PR Test (.github/workflows/pr-test.yml). PR Test resolves a policy and an image, runs the two CPU stages, and then the GPU stages, which are gated on stage-a-cpu succeeding so a formatting or import error does not burn GPU time. A PR that touches docker/Dockerfile, docker/build.py, docker/verify_transformer_engine.py, docker/patch/** or requirements.txt additionally builds the image first and runs every GPU suite inside it.

Registering a test

Selection is declared in the test file, never in the workflow YAML.
  • CPU tests go in tests/fast/. Every test_*.py there is auto-registered as a CPU test in stage-a-cpu with no labels, and runs on every PR. A register_cuda_ci under tests/fast/ is a hard error; move the file to tests/fast-gpu/.
  • Everywhere else, register explicitly. One top-level call per file:
register_cpu_ci allows empty labels for always-on CPU coverage; register_cuda_ci and register_rocm_ci require a non-empty domain-label list. All three also accept nightly=True (nightly, weekly, and release cadence only) and disabled="<reason + issue link>" (reported as skipped rather than deleted). The calls are parsed from the AST, so they must be top-level, literal, and unaliased. The runner scans tests/fast, tests/fast-gpu, tests/e2e and tests/ci for test_*.py, and a file outside tests/fast/ with no registration fails collection with No CI registry found. Suites are stage-<tier>-<gpus>-<hw>; pick the one an existing test like yours uses, because a typo’d suite has no job and silently never runs.

Verify it actually runs

Your file must appear under Enabled N test(s). The command also validates registration across every discovered test, so it fails here if any file is missing its declaration. Add --nightly when checking a nightly=True registration. On the PR, the matching stage job prints the same plan in its Resolve suite plan step.

Labels

Labels are how a PR opts into the expensive matrix. A test’s labels=["megatron"] is triggered by the GitHub label run-ci-megatron: the workflow forwards the labels, Python strips the run-ci- prefix and intersects with each test’s list. The canonical set lives in tests/ci/labels.py, and a value outside it is a collection-time error. If your fork PR sits waiting for approval, that is GitHub holding first-time contributor runs; any maintainer-applied run-ci-* label doubles as the approval.

PR-description CI tags

Three directives are read out of the PR description itself, one per line, matched at the start of a line: For the two ref directives, <ref> is a branch or commit, and the shorthand #1234 resolves to refs/pull/1234/head, which is how you test against an unmerged SGLang or Megatron-LM PR:
Precedence, when several sources disagree: a workflow_dispatch input wins, then the PR-description line, then the default. ci-image-tag: has one more rule: on a PR that built its own image, the fresh pr-<number> tag outranks the directive, so a docker PR always tests what it just built.

PR conventions

Commit subjects follow conventional commits, under 70 characters, and the body explains why:
Before marking a PR ready for review:
  • pre-commit run --all-files is clean.
  • pytest tests/fast passes, plus tests/fast-gpu if you have a GPU.
  • New behavior has a test, registered where CI will find it (verified with --list-only).
  • A new flag appears in CLI Reference, and python3 train.py --help still parses.
  • A change to a doc-dev: governed file updates its document in the same PR.
  • The PR description carries the CI directives your change needs.
Commenting /run-lint on a PR runs the hooks in CI and pushes the autofixes back to your branch, which is the quick way out of a red pre-commit job.

Issue triage

Comment to claim an issue before you start. For an infra failure or a flake, file the issue with the job URL, the runner name, the suite, and the log line, so a maintainer can map it to a host.

Where to ask