Skip to content

docs: add 2026 H2 improvement plan for Creature Lab - #16

Merged
iodriller merged 11 commits into
mainfrom
claude/creature-lab-improvements-t9uk9f
Jul 7, 2026
Merged

docs: add 2026 H2 improvement plan for Creature Lab#16
iodriller merged 11 commits into
mainfrom
claude/creature-lab-improvements-t9uk9f

Conversation

@iodriller

@iodriller iodriller commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Add a research-grounded improvement plan that keeps Creature Lab distinct
from Agentarium. Prioritizes upgrading the run report (self-contained HTML
run card, before/after comparison, reproducibility block, integrated zoo
gallery) followed by terrain library, robustness/sim2sim reporting,
quality-diversity gallery, a sharpened offline-first LLM design loop, and
packaging/trust work. Grounded in a scan of EvoGym, Revolve2, RoboMorph,
and reproducibility-card conventions.

claude added 11 commits July 7, 2026 15:50
Add a research-grounded improvement plan that keeps Creature Lab distinct
from Agentarium. Prioritizes upgrading the run report (self-contained HTML
run card, before/after comparison, reproducibility block, integrated zoo
gallery) followed by terrain library, robustness/sim2sim reporting,
quality-diversity gallery, a sharpened offline-first LLM design loop, and
packaging/trust work. Grounded in a scan of EvoGym, Revolve2, RoboMorph,
and reproducibility-card conventions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac8LrapgJhth67XouC6QtB
…Phase R)

Implement Phase R of the 2026 improvement plan: the report is now the
highest-fidelity artifact in the loop.

- creature_lab/reports_html.py: report_to_html renders a single offline HTML
  file with a score-breakdown bar, signal sparklines (CoM height, score,
  joint energy), a top-down root-path SVG plot, diagnosis, warnings,
  improvement lineage, and reproducibility info, plus an optional embedded
  GIF preview (data: URI, no network). comparison_to_html overlays two runs'
  root paths and renders a signed signal-delta table.
- reports.py: build_report now includes a reproducibility block (schema/lab
  version, timestep, seed, hashes, backend, and a runnable reproduce
  command), also rendered into the Markdown report. Added
  build_report_bundle (report + raw trace/creature for HTML renderers) and
  build_comparison (score/signal deltas between two reports).
- CLI: `report --html`, `compare --html` (writes a diff report instead of
  opening Viser), and `gallery build --zoo` now also emits index.html with
  baseline-vs-current score coloring per creature.
- 11 new tests covering HTML rendering, no-creature/no-media degradation,
  comparison deltas, and CLI wiring; verified no external URLs are ever
  embedded. Full suite green (252 passing), ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac8LrapgJhth67XouC6QtB
Implement Phase 1 of the 2026 improvement plan: slope/steps/gaps/rough
terrain, deterministic and identical in shape on both simulator backends.

- TaskSpec.terrain gains a TerrainType enum (slope/steps/gaps/rough) and the
  per-type parameters (slope_angle, step_height/length, gap_width/period,
  roughness/seed), with a gap_width < gap_period cross-check.
- creature_lab/terrain.py: pure, backend-agnostic heightfield generation
  (heightfield_grid, heightfield_range, normalized_heightfield_data,
  flatten_for_heightfield_api) so PyBullet and MuJoCo build the same ground
  shape from the same TaskSpec. gaps terrain keeps a solid platform around
  the origin so a creature always spawns on ground regardless of gap phase.
- PyBulletBackend builds a GEOM_HEIGHTFIELD body; MuJoCoBackend builds a
  <hfield> asset (export/mjcf.py) and fills model.hfield_data at runtime.
  Both engines turned out to auto-recenter/reindex heightfield data in ways
  their docs don't state plainly, and share the same axis convention -
  verified empirically (a PyBullet raycast probe, a MuJoCo free-falling
  probe body) before trusting the physics, rather than assuming it worked
  from the first pass that ran without crashing.
- Three new quadruped zoo tasks (slope_climb, step_over, gap_cross) with
  measured baselines.
- 32 new tests across terrain math, both backends, MJCF export, and the zoo.
  Full suite green (284 passing), ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac8LrapgJhth67XouC6QtB
Implement Phase 2 of the 2026 improvement plan: measure the "specs are
portable, physics is backend-dependent" contract instead of just stating it.

- creature_lab/robustness.py: pure perturbation + trial-statistics module,
  following evolve.py's callback-injection pattern (never touches physics
  directly). Deterministically jitters part masses and terrain friction
  under a seeded RNG and re-validates the result.
- CLI: `robustness runs/<id> --trials N` re-simulates under small seeded
  perturbations and reports score mean/std/min/max and fail rate - reveals
  gaits that only work for the exact recorded parameters.
- CLI: `sim2sim runs/<id>` runs the same creature/task on PyBullet and
  MuJoCo and reports the score gap and mean root-position divergence
  (reusing viewers/overlays.root_path). Running it on the packaged
  quadruped surfaced a real gap (PyBullet 0.57 vs. MuJoCo ~0.00 for the
  same open-loop gait) - the kind of result this phase exists to surface.
- Both commands take --save to write a reportable run; build_report and
  both report renderers (Markdown, HTML) grow an optional
  Robustness/Sim2Sim section when robustness.json/sim2sim.json are present.
- 20 new tests (pure perturbation/statistics math + CLI smoke tests, gated
  on the sim/mujoco extras). Full suite green (296 passing), ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac8LrapgJhth67XouC6QtB
Implement Phase 3 of the 2026 improvement plan: a QD search's most
compelling output - the diversity of solutions - was previously invisible.

- evolve --strategy map_elites now persists each filled cell's CreatureSpec
  into archive.json (previously only score/features were saved, discovered
  while implementing archive export - there was nothing to export yet).
- CLI: `archive show <run>` prints filled cells as a ranked table; --html
  renders a red-to-green scored heatmap (creature_lab.reports_html.
  archive_to_html); --html --task <task.json> additionally renders a
  replay GIF per cell (reusing render_trace/write_animation).
- CLI: `archive export <run> --cell row,col --out spec.json` pulls one
  elite out as a standalone, editable CreatureSpec - feeds back into
  scaffold/ask/evolve like any other spec. Verified round-trip on a real
  60-attempt map_elites run.
- 8 new tests (heatmap rendering, empty-archive handling, GIF embedding,
  CLI show/export, unknown-cell error). Full suite green (301 passing),
  ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac8LrapgJhth67XouC6QtB
…olve strategy (Phase 4)

Implement Phase 4 of the 2026 improvement plan, offline-first throughout.

- agents/loop.py: Observation gains a `diagnosis` field; design_loop gains
  an optional `diagnose` callback invoked on the current best before every
  proposal. agents/prompts.py includes "diagnosed issues: ..." in the
  prompt whenever a pattern is detected.
- CLI: `ask` wires a diagnose_fn (reusing diagnosis.diagnose) into
  design_loop, so both the online LLM and the offline policy's Observation
  see *why* the creature is scoring the way it is, not just the score.
- evolve.py: new `llm_mutate` mutate_fn that proposes one edit through the
  same offline RandomToolPolicy + validated tool layer used by `ask`,
  reseeded per-call from the caller's rng so --seed stays reproducible.
  CLI: `evolve --strategy llm` dispatches to hill_climb with this operator;
  each attempt's rationale is saved into lineage.json's node `note` field
  (shown by `creature-lab lineage`) since that's the artifact this command
  already produces, rather than agent.json (which belongs to `ask`).
- Verified end-to-end: --strategy llm improved the packaged quadruped from
  0.57 to 0.86 in 10 attempts and reproduces exactly for a fixed seed.
- 20 new tests across the design loop, prompts, evolve mutator, and CLI.
  Full suite green (311 passing), ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac8LrapgJhth67XouC6QtB
Implement Phase 5 of the 2026 improvement plan, minus the one step that
needs a human: publishing to PyPI requires real maintainer credentials, so
that step is left for a maintainer to run (uv build && uv publish) rather
than attempted here.

Verified rather than assumed:
- Built the wheel and diffed its contents against the source tree - all
  zoo creatures/tasks/baselines (including the new terrain tasks) package
  correctly.
- Installed the wheel into a clean venv outside the checkout and ran
  `creature-lab zoo run quadruped --task slope_climb` from an unrelated
  directory: the score matched the committed baseline exactly, confirming
  packaged data actually works standalone (not just "should work").

Added:
- A real, measured MuJoCo baseline for every packaged zoo creature/task
  pair (baselines/<task>.mujoco.json, 17 total) - previously only PyBullet
  baselines existed. zoo_baseline() takes an optional `backend` param to
  pick the right file.
- Found and fixed a real bug this surfaced: `bench --zoo --backend mujoco`
  was comparing MuJoCo scores against the PyBullet baseline regardless of
  --backend, so the pass/fail threshold was silently wrong for every
  non-default-backend benchmark run.
- CHANGELOG.md, linked from README.md and pyproject.toml; docs/ROADMAP.md
  updated to reflect what's done vs. what needs a maintainer.
- 6 new tests. Full suite green (314 passing), ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac8LrapgJhth67XouC6QtB
All of Phase R and Phases 1-5 landed this session; update the sequencing
table with final status instead of the original dependency-ordering guess.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac8LrapgJhth67XouC6QtB
…eport (Phase A)

Fixes the highest-priority gap from the post-implementation review: replaying
or exporting a run on non-flat terrain (slope/steps/gaps/rough) showed the
creature floating above or sinking into a flat floor, even though the
recorded poses were correct — only the ground was misrepresented.

- creature_lab/terrain.py: new height_at() — nearest-cell terrain height
  lookup for visual overlays (not precise enough for physics, which is why
  the backends use the full grid instead).
- pybullet_backend.render_trace() takes an optional `task` and reuses the
  existing _build_ground() helper to draw the real heightfield shape
  instead of always loadURDF("plane.urdf"). Verified with a real pixel-diff
  test: >50% of pixels differ between a flat and a gaps-terrain render of
  the same static scene.
- viewers/viser_viewer.py: build_scene() draws a trimesh heightfield mesh
  for non-flat terrain (a flat grid otherwise); add_debug_overlays() places
  the root-path dots at the actual terrain height via height_at() instead
  of a flat z=0.005, verified against the slope formula in a headless-viser
  test.
- Threaded `task`/`task_spec` through every render_trace call site (export,
  gallery build, archive show --task) and into report_to_html (the run
  report's embedded GIF), so a run's terrain is drawn everywhere it's
  visualized, not just simulated.
- 11 new tests: height_at math, render_trace terrain pixel-diff, headless
  viser floor-mesh-vs-grid and root-path-height assertions, and a CLI
  wiring smoke test. Full suite green (325 passing), ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac8LrapgJhth67XouC6QtB
…(Phase B)

Closes finding #3 from the post-implementation review: a report or
`inspect` gave no indication of which terrain a run used, even though the
terrain library (Phase 1) had been shipped for a while.

- creature_lab/terrain.py: new describe_terrain() — one-line human summary
  per terrain type (e.g. "slope (angle=0.2 rad, friction=1)"), tested
  against every terrain type.
- `inspect` (table and --json) and every report renderer (build_report,
  report_to_markdown, report_to_html) now show the terrain.
- docs/KNOWN_ISSUES.md: a living table for latent gaps and deliberate
  limitations found in review (diagnosis terrain-awareness, the finite
  heightfield extent, the rows==cols axis-mapping assumption, and CI not
  wheel-testing) plus the accepted-limitations this repo already lives
  with (backend-dependent physics, gaps-as-deep-pit) — so findings have a
  home instead of being re-discovered or lost between sessions.
- 8 new tests. Full suite green (333 passing), ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac8LrapgJhth67XouC6QtB
…s (Phase C)

Closes findings #4, #6, and part of #7 from the post-implementation review.

- archive show --html --task now embeds per-cell replay GIFs as data: URIs
  (matching the run report's embedded GIF) instead of writing them to a
  sibling directory the page silently breaks without if moved.
- CI builds the wheel, installs it into a fresh venv, and runs
  doctor + zoo run against the installed package (not the checkout) on
  every push/PR - verified locally first (reproduces the exact committed
  baseline score, 0.5736, from a clean install). Previously this was only
  checked once by hand in Phase 5 and could regress silently.
- validate_episode_inputs warns when a task's target lies outside the
  generated terrain's finite extent on non-flat terrain - a concrete,
  checkable case of the "finite heightfield extent" known issue.
  Target-less locomotion tasks can't be checked this way (no way to derive
  expected travel from the spec alone), so that part stays open and
  documented.
- terrain.py asserts DEFAULT_ROWS == DEFAULT_COLS at import time: the
  PyBullet/MuJoCo heightfield axis convention was only verified empirically
  for a square grid, so changing the constants to a non-square shape now
  fails loudly instead of silently swapping which world axis the terrain
  varies along.
- docs/KNOWN_ISSUES.md and CHANGELOG.md updated to reflect what's closed
  vs. still open.
- 5 new tests. Full suite green (338 passing), ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac8LrapgJhth67XouC6QtB
@iodriller
iodriller merged commit f6c9a2f into main Jul 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants