Skip to content

Add closed-loop atomic-action execution runner - #449

Merged
yuecideng merged 6 commits into
mainfrom
feat/atomic-action-pr2a-execution-runner
Aug 9, 2026
Merged

Add closed-loop atomic-action execution runner#449
yuecideng merged 6 commits into
mainfrom
feat/atomic-action-pr2a-execution-runner

Conversation

@yuecideng

@yuecideng yuecideng commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Description

This PR adds a closed-loop execution layer for typed atomic-action plans. It introduces a non-blocking ExecutionRunner, explicit observation/command/clock ports, command acknowledgement and cancellation semantics, trajectory timing propagation, safe hold behavior, and a simulation adapter that preserves batched environment correlation.

The runner closes the gap between planning/recovery contracts and real simulation execution. GraspGoal.grasp_xpos now accepts a late-bound SceneEntityPose, allowing one executing PickUp action to track a versioned grasp target. The moving-target tutorial spawns a cube in front of the robot, moves it sideways during PickUp, observes dynamic_goal_changed, replans the complete approach/close/lift trajectory, and physically lifts the relocated cube.

Review follow-ups correct waypoint arrival scheduling, fall back to Robot.get_proprioception()["qf"] when direct effort observation is unavailable, forward pure-hold batches to the simulator, and replace dispatch assertions with explicit validation errors.

Fixes: N/A (no linked issue).

Type of change

  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking correction to execution timing and simulator observation)

Validation

  • black --check --diff --color ./ — 892 files unchanged
  • pytest -q tests/sim/atomic_actions — 178 passed, 1 skipped, 3 deselected
  • Built-in action contract tests — 34 passed
  • Moving-target headless simulation — emitted dynamic_goal_changed, replanned the active PickUp, acquired the moved cube, and lifted it by 0.135 m
  • Static-target control simulation — acquired and lifted the cube by 0.136 m
  • Sphinx HTML build succeeded (664 repository-wide warnings)
  • Python compile, structural API, and diff checks passed

Screenshots

Not applicable; the tutorial runs headlessly and can record its simulation video.

Checklist

  • I have run the black . command to format the code base.
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix or feature works
  • Dependencies have been updated, if applicable. (No dependency-file changes required.)

@yuecideng yuecideng added enhancement New feature or request atomic action atomic action related functionality motion gen Things related to motion generation for robot labels Aug 2, 2026
@yuecideng
yuecideng force-pushed the feat/atomic-action-pr2a-execution-runner branch from 1794d02 to 00456c8 Compare August 3, 2026 03:37
@yuecideng
yuecideng marked this pull request as ready for review August 3, 2026 03:37
@skywhite1024
skywhite1024 force-pushed the refactor/atomic-action-pr1-contracts branch from 3e13cce to 172c68c Compare August 8, 2026 08:09
Base automatically changed from refactor/atomic-action-pr1-contracts to main August 9, 2026 11:31
@yuecideng
yuecideng force-pushed the feat/atomic-action-pr2a-execution-runner branch from 00456c8 to 0bbeb2d Compare August 9, 2026 12:17
Copilot AI lite review requested due to automatic review settings August 9, 2026 12:17
@greptile-apps

greptile-apps Bot commented Aug 9, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds a controller-independent closed-loop runner, simulation adapter, acknowledgement and safe-stop lifecycle, late-bound PickUp targets, and supporting documentation and tests.

  • Introduces observation, command-sink, and execution-clock ports around ExecutionSession.
  • Corrects multi-waypoint arrival scheduling by applying each next arrival interval after the preceding command.
  • Adds simulation execution support and moving-target PickUp recovery.
  • Extends GraspGoal to accept late-bound SceneEntityPose targets.

Confidence Score: 4/5

The PR is not yet safe to merge because single-waypoint trajectories can still undergo terminal tracking validation before the controller has had a meaningful settling interval.

A one-waypoint trajectory retains dt[:, 0] as its terminal hold; when that standard initial interval is zero, the runner waits only minimum_cycle_time before checking terminal tracking error, so an in-flight controller can trigger unnecessary replanning and exhaust recovery.

Files Needing Attention: embodichain/lab/sim/atomic_actions/execution.py, embodichain/lab/sim/atomic_actions/runner.py

Important Files Changed

Filename Overview
embodichain/lab/sim/atomic_actions/runner.py Adds non-blocking scheduling, controller acknowledgement handling, effect-verification boundaries, and cancel-then-hold safety behavior.
embodichain/lab/sim/atomic_actions/execution.py Corrects multi-waypoint interval placement, but the previously reported single-waypoint zero-settling behavior remains.
embodichain/lab/sim/atomic_actions/sim_adapter.py Adds simulation-backed observation, command, hold, cancellation, and deterministic clock integration.
embodichain/lab/sim/atomic_actions/primitives/pick_up.py Extends grasp goals to resolve late-bound scene poses and register dynamic recovery dependencies.
tests/sim/atomic_actions/test_runner.py Adds coverage for runner scheduling, acknowledgement, cancellation, verification, and batched behavior.

Sequence Diagram

sequenceDiagram
    participant App
    participant Runner as ExecutionRunner
    participant Adapter as Observation/Command/Clock
    participant Session as ExecutionSession
    App->>Runner: step() / run_until_blocked()
    Runner->>Adapter: observe(task_state)
    Adapter-->>Runner: PlanningContext
    Runner->>Session: tick(context, effect_success)
    Session-->>Runner: ExecutionTick + JointCommand
    Runner->>Adapter: send(command)
    Adapter-->>Runner: acknowledgement
    Runner->>Adapter: sleep(hold_duration)
    alt failure or cancellation
        Runner->>Adapter: cancel()
        Runner->>Adapter: hold(observed_position)
    end
Loading

Reviews (6): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

Comment thread embodichain/lab/sim/atomic_actions/runner.py

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a closed-loop execution layer around typed atomic-action ExecutionSessions by introducing a controller/simulation-facing ExecutionRunner plus a simulation adapter that implements observation, command dispatch, and deterministic clock advancement. It completes the runtime loop implied by the typed planning/recovery contracts (stacked on #448) and documents a tracking-error recovery tutorial.

Changes:

  • Introduces ExecutionRunner (+ config, ports, acknowledgements, cancellation, and effect-verification blocking/resume behavior) as the session’s outer execution lifecycle.
  • Adds SimulationExecutionAdapter implementing ObservationProvider, CommandSink, and ExecutionClock for deterministic headless simulation stepping.
  • Adds tests, tutorial script, and documentation/API/agent-context updates covering runner usage and tracking-error recovery.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/sim/atomic_actions/test_sim_adapter.py New unit tests validating simulation adapter observation, dispatch semantics, stable ID correlation, and timing.
tests/sim/atomic_actions/test_runner.py New unit tests validating non-blocking scheduling, acknowledgements, safe-stop behavior, replanning on tracking error, and effect verification boundaries.
scripts/tutorials/atomic_action/tracking_error_recovery.py New runnable tutorial demonstrating injected tracking error, replanning, and completion in headless simulation.
embodichain/lab/sim/atomic_actions/sim_adapter.py Adds simulation adapter implementing observation/command/clock ports for ExecutionRunner.
embodichain/lab/sim/atomic_actions/runner.py Adds ExecutionRunner and related protocols/types for closed-loop scheduling and controller interaction.
embodichain/lab/sim/atomic_actions/execution.py Exposes ExecutionSession.latest_context for runner safe-hold behavior and failure fallback.
embodichain/lab/sim/atomic_actions/init.py Exports new runner and adapter APIs as part of the public atomic-actions module surface.
docs/source/tutorial/atomic_actions.rst Updates tutorial docs to use ExecutionRunner + adapter and adds the new recovery example entry point.
docs/source/overview/sim/atomic_actions/index.md Updates overview to include runner boundary and clarifies responsibility split between session/runner/adapters.
docs/source/api_reference/embodichain/embodichain.lab.sim.atomic_actions.rst Adds API reference entries for runner, ports, adapter, and related enums/types.
agent_context/topics/atomic-actions/atomic-actions.md Updates agent context to include runner/ports responsibilities and usage patterns.
agent_context/MAP.yaml Updates topic keywords and file references to include runner and sim adapter.
.agents/skills/add-atomic-action/SKILL.md Updates development skill guidance to reference runner/adapter as the execution integration path.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread embodichain/lab/sim/atomic_actions/sim_adapter.py
Copilot AI review requested due to automatic review settings August 9, 2026 12:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

Suppressed comments (2)

embodichain/lab/sim/atomic_actions/runner.py:674

  • ExecutionRunner._dispatch() uses assert command is not None to validate required inputs. Assertions are stripped with python -O, which would turn these into potential None dispatches (and harder-to-diagnose failures). Prefer explicit checks that raise an exception (which you already convert into a rejected acknowledgement).
            if operation is CommandOperation.SEND:
                assert command is not None
                acknowledgement = self._command_sink.send(
                    command,
                    timeout=self.cfg.command_timeout,
                )
            elif operation is CommandOperation.HOLD:
                assert command is not None
                acknowledgement = self._command_sink.hold(

embodichain/lab/sim/atomic_actions/sim_adapter.py:191

  • SimulationExecutionAdapter.send() returns early when active_mask is all-false, so it does not write the provided (hold) targets even though the docstring says inactive rows are written with active rows to avoid stale tracking. This can leave the controller/sim tracking an old target if a caller uses send() for a pure-hold command.
            self._validate_command(command)
            if not command.active_mask.any():
                return CommandAcknowledgement.accepted_ack("No active rows.")

@greptile-apps

greptile-apps Bot commented Aug 9, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Start a greploop in Codex and it will work through the open comments and keep going until this PR reviews clean.

Copilot AI review requested due to automatic review settings August 9, 2026 13:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

Suppressed comments (1)

embodichain/lab/sim/atomic_actions/runner.py:407

  • ExecutionRunner.step() returns/records the PlanningContext produced by ObservationProvider before ExecutionSession.tick() replaces its task state with the session’s verified TaskState. This can make RunnerStep.context (and the stored effect-verification context) inconsistent with tick.task_state if an ObservationProvider doesn’t faithfully echo the provided task_state.

Since ExecutionSession.tick() always overwrites context.task (see execution.py:310-315), it’s safer for the runner to switch to session.latest_context after tick() and use that for _last_context/_update_effect_boundary and the returned RunnerStep.

        try:
            tick = self._session.tick(context, effect_success=effect_success)
        except Exception as exc:
            return self._fail(
                f"Execution session failed: {type(exc).__name__}: {exc}",

Copilot AI review requested due to automatic review settings August 9, 2026 13:51
Comment thread embodichain/lab/sim/atomic_actions/execution.py

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated no new comments.

Suppressed comments (1)

embodichain/lab/sim/atomic_actions/runner.py:595

  • In run_until_blocked(), the sleep duration is taken from result.wait_duration computed before calling on_step. If on_step is slow or (as in moving_target_recovery.py) advances the injected clock, self._clock.sleep(result.wait_duration) can oversleep and delay the next due cycle. Recompute the remaining wait from the current clock time right before sleeping so callback time/clock advancement is accounted for.
            if result.wait_duration > 0.0:
                try:
                    self._clock.sleep(result.wait_duration)
                except Exception as exc:

Copilot AI review requested due to automatic review settings August 9, 2026 14:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings August 9, 2026 14:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated no new comments.

Suppressed comments (1)

embodichain/lab/sim/atomic_actions/execution.py:642

  • hold_duration is computed from dt[:, waypoint_index + 1], which means TimedTrajectory.dt[:, 0] is now effectively ignored (waypoint 0 is always dispatched immediately). Because TimedTrajectory.duration must equal dt.sum(dim=1), non-zero dt[:, 0] would make the execution schedule inconsistent with the trajectory’s own timing metadata. Consider explicitly validating dt[:, 0] == 0 (or documenting/enforcing it elsewhere) to avoid silent timing drift for callers that provide custom dt tensors.
        next_waypoint_index = min(
            waypoint_index + 1,
            phase.trajectory.waypoint_count - 1,
        )
        hold_duration = phase.trajectory.dt[:, next_waypoint_index]

@yuecideng
yuecideng merged commit 76ad0f3 into main Aug 9, 2026
6 checks passed
@yuecideng
yuecideng deleted the feat/atomic-action-pr2a-execution-runner branch August 9, 2026 16:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

atomic action atomic action related functionality enhancement New feature or request motion gen Things related to motion generation for robot

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants