Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ trajectory so breaking changes can land in any minor release until the 1.0 stabi

### Changed

- **Internal restructuring** (no behaviour change): the play-mode
shortcut-retargeting key list moved off the private
`cli/__init__.py:_PLAY_RETARGETED_KEYS` constant onto
`SimulationCfg.play_retargeted_keys()` (a static method on the domain
config in `genelab.configs`). The CLI's `env.` → `play_env.` retarget
loop now calls the method; the set of play-retargetable simulation
override paths (`env.simulation.{vis,gpu,steps,dt}`) lives next to the
`SimulationCfg` fields the `--vis` / `--gpu` / `--steps` / `--dt`
shortcuts target. `genelab play --help` is unchanged (R0.1 snapshot
gate green); `configs.py` stays torch-free at import (invariant #5).
Lands as ROADMAP §9 PR R3.2 — completes ADR-0005 (R3).
- **Internal restructuring** (no behaviour change): `--eval-*` runner-arg
parsing for in-training eval moved from `cli/__init__.py:_build_eval_callback`
onto the domain config as `EvalCallbackCfg.from_args(runner_args) ->
Expand Down
12 changes: 2 additions & 10 deletions src/genelab/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
render_registry,
)
from genelab.cli._scaffold import create_project_skeleton
from genelab.configs import apply_overrides
from genelab.configs import SimulationCfg, apply_overrides
from genelab.registry import (
TASKS,
load_bundled_asset_zoo,
Expand Down Expand Up @@ -80,14 +80,6 @@ class _RegistryKindArg(str, Enum):

_AGENT_KINDS: Final[frozenset[str]] = frozenset({"zero", "random", "trained"})

_PLAY_RETARGETED_KEYS: Final[tuple[str, ...]] = (
"env.simulation.vis",
"env.simulation.gpu",
"env.simulation.steps",
"env.simulation.dt",
)


_RUN_FLAGS_HELP: Final[str] = """\
Shorthand flags rewritten into env overrides:

Expand Down Expand Up @@ -556,7 +548,7 @@ def _configured_task(
# task's play_env when one is configured. Keeps `genelab play TASK --vis` working
# without forcing users to spell `play_env.simulation.vis`.
if command == "play" and getattr(task.cfg, "play_env", None) is not None:
for short_key in _PLAY_RETARGETED_KEYS:
for short_key in SimulationCfg.play_retargeted_keys():
if short_key in overrides:
overrides[short_key.replace("env.", "play_env.", 1)] = overrides.pop(short_key)

Expand Down
19 changes: 19 additions & 0 deletions src/genelab/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,25 @@ class SimulationCfg:
# raise ``decimation`` if that's not what you want.
render_fps: int | None = 60

@staticmethod
def play_retargeted_keys() -> tuple[str, ...]:
"""Override paths the CLI rewrites ``env.`` → ``play_env.`` in play mode.

The ``--vis`` / ``--gpu`` / ``--steps`` / ``--dt`` shorthand flags expand
to ``env.simulation.<field>`` overrides. When a task defines a separate
``play_env``, ``genelab play TASK --vis`` should target *that* env, so the
CLI retargets these keys onto ``play_env.simulation.<field>``. Owning the
list here (rather than as a private constant in ``cli/__init__.py``) keeps
the set of play-retargetable simulation overrides next to the fields
themselves (ADR-0005 / R3.2).
"""
return (
"env.simulation.vis",
"env.simulation.gpu",
"env.simulation.steps",
"env.simulation.dt",
)


@dataclass
class InteractiveSceneCfg:
Expand Down
36 changes: 36 additions & 0 deletions tests/test_configs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Tests for ``genelab.configs`` domain-config helpers.

Currently covers ``SimulationCfg.play_retargeted_keys`` (ROADMAP §9 R3.2 /
ADR-0005) — the set of override paths the CLI rewrites ``env.`` →
``play_env.`` in play mode. The list moved off a private constant in
``cli/__init__.py`` onto the domain config so it lives next to the
``SimulationCfg`` fields the shortcut flags target.
"""

from __future__ import annotations

from genelab.configs import SimulationCfg


def test_play_retargeted_keys_exact_set() -> None:
"""The four ``env.simulation.*`` shortcut-override paths, verbatim and ordered."""
assert SimulationCfg.play_retargeted_keys() == (
"env.simulation.vis",
"env.simulation.gpu",
"env.simulation.steps",
"env.simulation.dt",
)


def test_play_retargeted_keys_target_real_simulation_fields() -> None:
"""Every retargeted key names an actual ``SimulationCfg`` field (no stale paths)."""
fields = SimulationCfg.__dataclass_fields__
for key in SimulationCfg.play_retargeted_keys():
assert key.startswith("env.simulation."), key
field_name = key.rsplit(".", 1)[1]
assert field_name in fields, f"{field_name!r} is not a SimulationCfg field"


def test_play_retargeted_keys_callable_on_class_and_instance() -> None:
"""Static method — same result whether called on the class or an instance."""
assert SimulationCfg.play_retargeted_keys() == SimulationCfg().play_retargeted_keys()
Loading