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:
- source
scripts/lib/site.env, then the run config - source each module named by the config's
TEMPLATE_MODULES - require the variables the runner cannot default
- resolve derived values and print the run configuration block
- run validation (
scripts/lib/preflight.sh) - 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 repoSCAFFOLD=<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 settingsHarbor 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 eligibilityA 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:
| Layer | Where | Example |
|---|---|---|
| Site | scripts/lib/site.env | registry address, kubeconfig path |
| Run config | scripts/<kind>/configs/*.env | model, data, topology, experiment name |
| Module default | scripts/templates/<dimension>/*.env | everything 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 checkAdding 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 checkThere 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.