Refactor atomic actions for typed planning and recovery - #448
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request refactors the simulation atomic-action stack from the legacy “target + execute + threaded world state” contract into a typed, side-effect-free planning API (AtomicAction.plan(...)) with an offline compiler (AtomicActionEngine.compile(...)) and a closed-loop execution runtime (AtomicActionEngine.start(...)/ExecutionSession.tick(...)), including explicit motion and recovery policies.
Changes:
- Introduces typed planning/execution contracts (
ActionInvocation,ActionBinding,PlanningContext,MotionPolicy,RecoveryPolicy,ActionPlan,StateDelta) and updatesAtomicActionEngineto compile invocations instead of running targets. - Migrates built-in atomic actions (primitives) from
execute()toplan()and replaces “targets” with typed “goals”, including late-bound scene-entity pose goals. - Updates tests, tutorials, benchmarks, examples, docs, and agent context to the new contracts.
Reviewed changes
Copilot reviewed 61 out of 61 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/sim/planners/test_curobo_planner.py | Updates cuRobo planner tests to use ActionInvocation + compile() and typed goals/policies. |
| tests/sim/atomic_actions/test_trajectory.py | Adds tests for success-mask normalization and “owned tensor” outputs at planner boundaries. |
| tests/sim/atomic_actions/test_trajectory_motion_source.py | Migrates motion-source tests from ActionCfg to MotionPolicy and validates plan-options forwarding behavior. |
| tests/sim/atomic_actions/test_motion_source_e2e.py | Updates motion-source E2E test to compile() with typed invocation/binding/policy. |
| tests/sim/atomic_actions/test_engine.py | Replaces run() sequencing tests with compiler/registry semantics and plan validation checks. |
| tests/sim/atomic_actions/test_engine_per_env.py | Adds execution-session recovery tests for dynamic goals, tracking error, timeouts, and effect verification. |
| tests/sim/atomic_actions/test_curobo_motion_source_e2e.py | Updates cuRobo E2E atomic action test to compile() with typed invocation. |
| tests/sim/atomic_actions/test_action_result_success.py | Removes obsolete tests tied to removed ActionResult/legacy config surface. |
| scripts/tutorials/atomic_action/press.py | Migrates tutorial to compile typed invocations and use MotionPolicy for sample counts. |
| scripts/tutorials/atomic_action/place.py | Migrates PickUp→Place tutorial to typed goals/bindings/policies and compile(). |
| scripts/tutorials/atomic_action/pickup.py | Migrates PickUp tutorial to typed goals/bindings/policies and compile(). |
| scripts/tutorials/atomic_action/move_joints.py | Migrates MoveJoints tutorial to typed joint goals and compile(). |
| scripts/tutorials/atomic_action/move_held_object.py | Migrates held-object transport tutorial to typed goals/bindings/policies and compile(). |
| scripts/tutorials/atomic_action/move_end_effector.py | Migrates MoveEndEffector tutorial to typed pose goals and compile(). |
| scripts/tutorials/atomic_action/hand_over.py | Migrates HandOver demo to typed goals/bindings/policies and compile(). |
| scripts/tutorials/atomic_action/coordinated_placement.py | Migrates coordinated placement demo to plan()/compile() semantics and projected context updates. |
| scripts/tutorials/atomic_action/coordinated_pickment.py | Migrates coordinated pickment demo to typed goal + invocation + compile(). |
| scripts/tutorials/atomic_action/assemble.py | Migrates assemble demo from AssembleTarget to AssembleGoal and compile(). |
| scripts/benchmark/atomic_action/press_benchmark.py | Migrates benchmark runner to compile() and typed invocations for timing/memory measurement. |
| scripts/benchmark/atomic_action/place_benchmark.py | Migrates benchmark runner to compile() and typed invocations with projected context threading. |
| scripts/benchmark/atomic_action/pickup_benchmark.py | Migrates benchmark runner to compile() and typed invocations. |
| scripts/benchmark/atomic_action/move_joints_benchmark.py | Migrates benchmark runner to compile() and typed invocations. |
| scripts/benchmark/atomic_action/move_held_object_benchmark.py | Migrates benchmark runner to compile() and typed invocations with projected context. |
| scripts/benchmark/atomic_action/move_end_effector_benchmark.py | Migrates benchmark runner to compile() and typed invocations. |
| examples/sim/planners/curobo_planner.py | Updates cuRobo example to use typed invocation/binding/policy with compile(). |
| embodichain/lab/sim/atomic_actions/targets.py | Removes legacy shared target contracts (ActionTarget-based), replaced by goal contracts. |
| embodichain/lab/sim/atomic_actions/primitives/press.py | Refactors Press from execute() to plan() and replaces PressTarget with PressGoal. |
| embodichain/lab/sim/atomic_actions/primitives/place.py | Refactors Place to plan() and replaces PlaceTarget/AssembleTarget with PlaceGoal/AssembleGoal plus StateDelta. |
| embodichain/lab/sim/atomic_actions/primitives/pick_up.py | Refactors PickUp to plan() and replaces GraspTarget with GraspGoal plus StateDelta. |
| embodichain/lab/sim/atomic_actions/primitives/move_joints.py | Refactors MoveJoints to plan() and replaces joint targets with typed joint goals. |
| embodichain/lab/sim/atomic_actions/primitives/move_held_object.py | Refactors MoveHeldObject to plan() and replaces HeldObjectPoseTarget with HeldObjectPoseGoal. |
| embodichain/lab/sim/atomic_actions/primitives/move_end_effector.py | Refactors MoveEndEffector to plan() and replaces EndEffectorPoseTarget with EndEffectorPoseGoal. |
| embodichain/lab/sim/atomic_actions/primitives/hand_over.py | Refactors HandOver to plan() and uses policy-driven sample budgeting + StateDelta effects. |
| embodichain/lab/sim/atomic_actions/primitives/coordinated_placement.py | Refactors CoordinatedPlacement to plan() with binding validation and StateDelta effects. |
| embodichain/lab/sim/atomic_actions/primitives/coordinated_pickment.py | Refactors CoordinatedPickment to plan() with binding validation and StateDelta effects. |
| embodichain/lab/sim/atomic_actions/primitives/_helpers.py | Updates helper utilities to operate on PlanningContext and return owned pose tensors. |
| embodichain/lab/sim/atomic_actions/primitives/init.py | Updates public primitive exports from targets to goals. |
| embodichain/lab/sim/atomic_actions/policies.py | Adds MotionPolicy and RecoveryPolicy configclasses used per invocation. |
| embodichain/lab/sim/atomic_actions/invocation.py | Adds the grounded ActionInvocation contract for deterministic planning/execution. |
| embodichain/lab/sim/atomic_actions/goals.py | Adds goal protocols and late-bound scene goal types (SceneEntityPose) with resolution helpers. |
| embodichain/lab/sim/atomic_actions/engine.py | Replaces run() with initial_context(), compile(), and start() plus strict plan/context validation. |
| embodichain/lab/sim/atomic_actions/effects.py | Adds declarative StateDelta expected effects with masked application semantics. |
| embodichain/lab/sim/atomic_actions/bindings.py | Adds ActionBinding to map semantic roles to embodiment resources. |
| embodichain/lab/sim/atomic_actions/actions.py | Removes legacy compatibility re-export facade. |
| embodichain/lab/sim/atomic_actions/init.py | Re-exports new typed planning/execution API surface and built-in goals/actions. |
| docs/source/api_reference/embodichain/embodichain.lab.sim.atomic_actions.targets.rst | Removes API docs page for deleted legacy targets module. |
| docs/source/api_reference/embodichain/embodichain.lab.sim.atomic_actions.rst | Updates API reference to new planning/execution contracts and goal-based primitives. |
| docs/source/api_reference/embodichain/embodichain.lab.sim.atomic_actions.primitives.rst | Updates primitives docs from target/execute/world-state to invocation/plan/context/effects. |
| agent_context/topics/atomic-actions/atomic-actions.md | Updates agent-facing atomic-actions context to the new typed planning + recovery runtime. |
| agent_context/MAP.yaml | Updates atomic-actions topic routing keywords and source-of-truth paths to new modules. |
Suppressed comments (1)
scripts/benchmark/atomic_action/place_benchmark.py:333
is_successis later used in Python boolean contexts (bool(is_success),is_success and ...,elif not is_success). Sinceplan_successis a tensor mask, this will crash at runtime unless reduced to a bool.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ), | ||
| ) | ||
| ) | ||
| is_success = result.plan_success |
| ) | ||
| ) | ||
| is_success, traj, final_state = result | ||
| is_success = result.plan_success |
| ), | ||
| ) | ||
| ) | ||
| is_success = result.plan_success |
| ) | ||
| ) | ||
| is_success, traj, _ = result | ||
| is_success = result.plan_success |
| elapsed, mem_delta, peak_gpu, result = timed_call( | ||
| lambda: atomic_engine.compile(steps) | ||
| ) | ||
| is_success = result.plan_success |
| ) | ||
| ) | ||
| is_success, traj, final_state = result | ||
| is_success = result.plan_success |
| ), | ||
| ) | ||
| ) | ||
| is_success = result.plan_success |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 62 out of 62 changed files in this pull request and generated no new comments.
Suppressed comments (8)
scripts/benchmark/atomic_action/place_benchmark.py:235
CompiledTrajectory.plan_successis a per-environment bool tensor. Using it directly in a Pythonif not is_successcondition will raise when the benchmark runs with more than one environment. Convert it to a scalar boolean (e.g.,all()+.item()) before branching.
scripts/benchmark/atomic_action/place_benchmark.py:333result.plan_successis a vector mask; later code usesbool(is_success)/elif not is_success, which will error when multiple envs are present. Store a scalaris_successboolean derived from the mask (typicallyall()for benchmarks) and keep the trajectory/state separate.
scripts/benchmark/atomic_action/move_held_object_benchmark.py:250plan_successis returned as a per-env tensor mask. Usingif not is_successwill raise for multi-env runs. Convert to a scalar boolean (e.g.,all().item()) before branching in this precondition helper.
scripts/benchmark/atomic_action/move_held_object_benchmark.py:348result.plan_successis a per-environment tensor mask but this function later treatsis_successas a boolean (e.g.,bool(is_success),elif not is_success). Convert to a scalar boolean derived from the mask to avoid ambiguous truth-value errors when multiple envs are used.
scripts/benchmark/atomic_action/pickup_benchmark.py:201result.plan_successis a per-environment boolean tensor, but this benchmark later usesbool(is_success)andif is_success ..., which will raise when more than one environment is active. Convert the mask to a scalaris_successboolean (e.g.,all().item()) before using it in control flow and reporting.
scripts/benchmark/atomic_action/move_end_effector_benchmark.py:146CompiledTrajectory.plan_successis a per-environment tensor mask. This benchmark treatsis_successas a Python boolean (bool(is_success),if is_success ...), which will fail for multi-env runs. Convert to a scalar boolean derived from the mask (e.g.,all().item()).
scripts/benchmark/atomic_action/move_joints_benchmark.py:155result.plan_successis a vector mask over environments. The code later treatsis_successas a boolean (bool(is_success),if is_success ...), which will raise when batch size > 1. Convert it to a scalar boolean (e.g.,all().item()) before branching/reporting.
scripts/benchmark/atomic_action/press_benchmark.py:599- This function returns
planning_successwhich is used as a boolean by callers, butresult.plan_successis a per-environment tensor mask. Convert to a scalar boolean (e.g.,all().item()) to avoid ambiguous truth-value errors if the benchmark is ever run with multiple environments.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 67 out of 67 changed files in this pull request and generated no new comments.
Suppressed comments (2)
scripts/benchmark/atomic_action/place_benchmark.py:240
result.plan_successis a per-environmenttorch.Tensor; usingif not is_successwill raiseRuntimeError: Boolean value of Tensor with more than one value is ambiguouswhen running withnum_envs > 1. Convert to a scalar with.all().item()(or otherwise aggregate) before using it in a Python boolean context.
scripts/benchmark/atomic_action/move_held_object_benchmark.py:253is_successis a per-environmenttorch.Tensor;if not is_successis invalid when there is more than one environment row. Use an explicit reduction (e.g.,.all().item()) before using it in a Python boolean context.
Description
This PR replaces the legacy atomic-action target/execution contract with a typed, side-effect-free planning model and a bounded closed-loop recovery runtime.
The previous API mixed target data, planner policy, predicted task state, and execution semantics. That made
execute()look like a controller operation even though it only generated trajectories, and it could not represent late-bound moving goals, verified effects, or recovery from tracking errors. This refactor establishes explicit boundaries between semantic intent, embodiment binding, planning, static compilation, and feedback-driven execution.Dependencies: None. No dependency files are changed.
Design references:
Highlights
ActionGoal,ActionBinding,ActionInvocation,MotionPolicy, andRecoveryPolicycontracts.RobotObservation, verifiedTaskState, versionedSceneSnapshot, and immutablePlanningContext.ActionPlan, phase metadata, full-robotTimedTrajectory, planner diagnostics, and declarativeStateDeltaeffects.AtomicAction.execute()with side-effect-freeAtomicAction.plan().AtomicActionEngine.run()with:compile()for offline trajectory composition and projected state;start()/ExecutionSession.tick()for incremental command generation and verified state updates.SceneEntityPosegoals and bounded recovery for tracking error, moving goals, phase timeout, planning failure, and effect-verification failure.add-atomic-actiondevelopment skill.Breaking changes
ActionTarget,WorldState,ActionResult,AtomicAction.execute(), andAtomicActionEngine.run().ActionInvocationandPlanningContext, then callplan(),compile(), orstart()as appropriate.MotionPolicyandRecoveryPolicyinstead of individual action configs.No compatibility layer is included by design.
Type of change
Validation
black .— 554 files unchangedpytest -q tests/sim/atomic_actions -m 'not requires_sim' tests/sim/planners/test_curobo_planner.py— 143 passed, 2 skipped, 5 deselectedfrom __future__ import annotations, and public-module export checks — passed for 49 changed Python filesgit diff --check origin/main...HEAD— passedadd-atomic-actionskill validation — passedA full Sphinx
-Wbuild was attempted during implementation but remains blocked by pre-existing repository-wide warnings outside the atomic-action module. The affected atomic-action pages import and render, and the affected toctree warning was fixed.Screenshots
Not applicable; this is an API and runtime refactor.
Checklist
black .command to format the code base.