Lego-RL

Contributing

The repository layout, the runner contract, and where to add a part

Lego-RL is assembled from replaceable parts. Most contributions add one part without touching the training loop.

Layout

scripts/
  train/  eval/  infer/     one runner each: <kind>.sh, _template.env, configs/, lib/
  templates/                composable modules: backend/ scaffold/ verl/ harbor/ runtime/
  lib/                      shared: site.env, preflight.sh, model_traits.sh, common_env.sh
src/
  verl_patch/               verl config overlays (lego_rl_*.yaml) and patches
  harbor_patch/             agents and environment patches applied to harbor
utils/                      index building, checkpoint merging, dataset conversion
webui/                      the dashboard server and its static build
docs/                       this site (fumadocs)

harbor and verl are cloned as siblings of the repo and installed editably by scripts/setup_env.sh, so a change in either is picked up without reinstalling.

The runner contract

Every runner does the same six things, in order:

  1. source scripts/lib/site.env, then the run config
  2. source each module named by the config's TEMPLATE_MODULES
  3. require the variables the runner cannot default
  4. resolve derived values and print the run configuration block
  5. run validation (scripts/lib/preflight.sh)
  6. launch

Steps 1–5 are exactly what --structure-only, --preflight-only and --dry-run stop after. Any change to configuration should be visible in step 4's output; that block is the contract with the person launching the run.

Where to add a part

An agent scaffold

An agent harness reaches the trainer through two files: a Harbor agent that knows how to drive it, and a template module that names it.

scripts/templates/scaffold/<name>.env    # HARBOR_AGENT_NAME, loop config, parser defaults
src/harbor_patch/agents/<name>/          # or an agent in the harbor repo

SCAFFOLD=<name> then selects it. Add the pairing rule to scripts/lib/preflight.sh so a wrong TOOL_CALL_PARSER fails before launch rather than 100 % of the way through a step, and add a row to Compatibility.

A sandbox backend

scripts/templates/backend/<name>.env     # environment class + its settings

Harbor already carries backends Lego-RL has never exercised (Daytona, E2B, Modal, Runloop). Wiring one up is mostly a template module plus the validation rules that make its failure modes loud.

A model preset

Two places decide how a checkpoint is treated:

scripts/lib/model_traits.sh    # MoE detection off config.json
scripts/lib/preflight.sh       # tool-parser rules, VRAM guidance, R3 eligibility

A task source

A task set is a directory of Harbor task instances plus a parquet index built by utils/create_task_index.py. See Data Preparation. Converters for new upstream datasets belong in utils/.

A failure signature

The most valuable contribution to an infrastructure project is usually a check. If a run failed in a way that took hours to diagnose, add the rule that would have caught it to scripts/lib/preflight.sh, and the symptom to the Failure Playbook.

Adding a configuration variable

A variable belongs in exactly one layer:

LayerWhereExample
Sitescripts/lib/site.envregistry address, kubeconfig path
Run configscripts/<kind>/configs/*.envmodel, data, topology, experiment name
Module defaultscripts/templates/<dimension>/*.enveverything with a sensible default

Defaults use : "${VAR:=value}" so a config can always override them, and : "${VAR:?message}" for values with no safe default. Put it in the module that owns the concept, not in the runner.

Editing this site

cd docs
npm install
npm run dev          # http://localhost:3000
npm run check        # docs lint + build + link check

Adding a page means an .mdx file under content/docs/ plus an entry in the enclosing meta.json. Conventions:

  • Titles are the shortest noun phrase that names the thing.
  • Every run-type guide follows the same shape: Prerequisites → Setup → Check → Run → Output → Dashboard → Cleanup.
  • Numbers from our cluster are labelled as observations, never as guarantees.
  • A command in a code block is a command you can paste, with a repo-root-relative path.

Deploying is bash docs/deploy_cloudflare_pages.sh. The site is a static export, so an edit that is not rebuilt and redeployed does not appear online.

Before opening a pull request

# 1. the config path still resolves and every structural rule holds
bash scripts/train/train.sh --structure-only scripts/train/examples/quickstart.env

# 2. docs lint + build + link check
cd docs && npm run check

There is no test suite yet, and no CI. Run validation is the closest thing the project has to one: a change that makes a config unlaunchable should fail at --structure-only, not at hour two of a run.

On this page