beagle is a PyTorch-like framework for agent-harness evolution, built on a vendored
xrlenv (vendor/xrlenv) that provides the containerized rollout infrastructure.
These are the hard conventions; follow them.
Do not introduce new environment variables. Anything a run needs should be an explicit flag or config field, not a hidden env var — env vars sneakily hide what a run actually uses. If a value can be passed as a flag, pass it as a flag.
Allowed env vars (do not add to this list without permission):
- The xrlenv-introduced ones already in use:
XRLENV_NODE_TOKEN,XRLENV_CONSUMER_TOKEN,XRLENV_OPERATOR_TOKEN,XRLENV_GRPC_HOST,XRLENV_GRPC_PORT,XRLENV_REGISTRY_STORAGE,XRLENV_MIRROR_REGISTRY_HOST,XRLENV_MIRROR_REGISTRY_PORT,XRLENV_PRIVATE_REGISTRY_STORAGE,XRLENV_PRIVATE_REGISTRY_HOST,XRLENV_PRIVATE_REGISTRY_PORT,XRLENV_PRIVATE_REGISTRY_HTTP_SECRET,DOCKERHUB_USER,DOCKERHUB_TOKEN,XRLENV_HOME,XRLENV_BENCHMARK_CACHE(formerlyXRLENV_HARBOR_CACHE, retired by xrlenv 2026-07-31). XRLENV_GROUP_ID— set by DarwinX from the typedxrlenv_group_idconfig field to scope a run's xrlenv registrations to a per-run group; consumed by the xrlenv runtime.- Credentials / secrets (API keys, tokens) — these legitimately come from the environment.
To introduce any other new env var, ask the maintainer first, with justification.
- Respect the original harness — honor its filesystem contract verbatim. Rollouts
flow through each benchmark's native harness via the xrlenv drop-in; the per-rollout
artifacts (harbor's
<job>/<trial>/{agent,verifier,artifacts,...}, verifierreward.txt) must be byte-compatible with upstream's output — never reshape into a house format. For harbor: useharbor.Job.create(JobConfig(...))+job.run()(the Job driver), not the low-levelSingleStepTrial+ a customrun.json— a known past failure. A trial must also carryagent/trajectory.jsonlike a native harbor trial: ATIF (harbor's Agent Trajectory Interchange Format) is beagle's canonical trajectory format — every agent's native trajectory is converted to ATIF by a per-format converter (beagle/benchmarks/trajectory.py, one converter per format, M+N) and written by the harness POST-JOB (HarborHarness. _emit_trajectories), NOT in the in-trial shim: on the xrlenv cluster the native stream is synced from the container to the host only after the agent step, so the shim runs too early. Don't invent a house trajectory format; align to ATIF. - Agents are benchmark-agnostic — no per-benchmark prompt templates. Do NOT couple an
agent's system prompt to the benchmark. Keying a
.j2per benchmark onbenchmark_nameis an N×M trap (a past failure): a new benchmark then has to touch every agent, and a new agent has to ship a template per benchmark. The benchmark supplies the task instruction; the agent runs it. Benchmark-specific framing, if any, lives on the benchmark side. - Experiment-copy rule. Every evolvable agent's
AgentSource.repopoints at an experiment copy we own; the evolver pushes candidate branches there, never the canonical upstream repo. - Token usage: normalize into
Usage, never fold cache away. When onboarding a new agent harness, its usage parser MUST map the native stream intobeagle/agents/core/usage.py:Usage(four disjoint buckets:input_uncached,cache_read,cache_write,output) and setTaskResult.tokens = usage.to_token_counts(). This keeps the cache split inresult.json/run.jsonso a downstream cost estimate can price each bucket (beagle stays pricing-agnostic — no price sheet in-repo). Watch the per-provider cache semantics, which differ: OpenAI-shaped streams reportcached_tokensas a subset ofprompt_tokens(fresh =prompt − cached); Anthropic-shaped ones report cache read/write in addition to input. A naive uniform+= cacheddouble-counts the OpenAI case. Invariant:prompt = input_uncached + cache_read + cache_write. (Legacyprompt/completion/totalstay for back-compat.) - Green-before-next: keep
pytest tests/passing; add a test per change.