Skip to content

Skip action_manager.process_action() when action is None - #11

Open
lanafi00 wants to merge 7 commits into
AccelerationConsortium:mainfrom
lanafi00:fix/action-manager-none-action
Open

lanafi00 wants to merge 7 commits into
AccelerationConsortium:mainfrom
lanafi00:fix/action-manager-none-action

Conversation

@lanafi00

@lanafi00 lanafi00 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Description

self.action_manager.process_action(action.to(self.device)) was crashing when action is None, which happens for any step whose StateMachine action sequence has no agent_assets (ie a pure SemanticActionCfg step like TurnOnHeaterCfg). I fixed this by adding a check to only run self.action_manager.process_action(action.to(self.device)) if action is not None.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • I have run the pre-commit checks with <FULL_PATH_TO_ISAACLAB>/isaaclab.sh --format
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the changelog and the corresponding version in the extension's config/extension.toml file
  • I have added my name to the CONTRIBUTORS.md or my name already exists there

@lanafi00
lanafi00 requested a review from kouroshD as a code owner September 3, 2026 02:39
@SissiFeng

Copy link
Copy Markdown
Collaborator

Request changes: please add regression coverage for the complete supported workflow path before this is merged.

The underlying None condition is reproducible, but the current patch only guards MatterixBaseEnv.step(). The standard runner still calls action.to(env.device) before env.step(), so a semantic-only workflow fails before reaching this guard.

Please complete the following:

  1. Add a StateMachine unit test for a sequence containing only TurnOnHeaterCfg:

    • StateMachine.step() returns None;
    • exactly one IsHeaterOn semantic action is emitted;
    • the sequence is marked successful.
  2. Add an end-to-end runner regression test showing that scripts/run_workflow.py can execute the semantic-only sequence without calling .to() on None.

  3. Add an Isaac runtime test for env.step(None, semantic_actions=...) verifying that:

    • action_manager.process_action() is not called;
    • the semantic action is applied exactly once;
    • subsequent action_manager.apply_action() calls do not apply zero or stale controller targets.
  4. Cover both important transitions:

    • immediately after environment reset → semantic-only action;
    • physical robot action → semantic action, where the robot must hold its previous target.
  5. Keep a mixed workflow regression test confirming that a sequence containing later robot actions still returns a valid hold tensor during its initial semantic step.

  6. Update the MatterixBaseEnv.step() and StateMachine.step() type annotations/docstrings to include None, and document what None means for physical control.

Please run these tests after rebasing onto current main and include the exact Isaac Lab/Isaac Sim versions in the test evidence.

@lanafi00
lanafi00 force-pushed the fix/action-manager-none-action branch from d06e964 to 3e0c024 Compare September 4, 2026 03:22
@SissiFeng

Copy link
Copy Markdown
Collaborator

@lanafi00

Thank you for the update. The core fix now covers both MatterixBaseEnv.step() and scripts/run_workflow.py, and the new StateMachine CPU tests pass locally. However, a few test gaps still need to be addressed before approval:

  1. Use a real robot action in the mixed-workflow regression test.
    The current test uses WaitCfg(agent_assets="robot") without an ActionSpaceInfo, producing a (2, 1) tensor, while the Franka environment expects an 8-dimensional action. Please use a genuine later robot action with FRANKA_IK_ACTION_SPACE, assert the complete action shape and values, and ideally pass the returned tensor through the environment’s action manager.

  2. Strengthen the Isaac runtime assertions.
    Comparing action_manager.action only checks the raw input buffer; it does not prove that the processed controller or actuator target applied during each decimation step is safe. Please verify the actual processed/controller target or the robot joint/EE target for both:

    • reset → semantic-only action;
    • physical robot action → semantic-only action, holding the previous target.
  3. Verify that the semantic action is applied exactly once.
    Checking that the heater is on only proves that the action was applied at least once. Please add a call-count assertion around the relevant semantic set_value() or equivalent application point.

  4. Make the end-to-end runner test bounded and automatable.
    proc.stdout.readline() can block beyond the stated timeout, and reaching EPISODE 2 only proves that the first episode completed. Please add a bounded runner option such as --max-episodes, require a normal process exit, and assert successful completion without relying on forced termination.

  5. Separate CPU and Isaac/GPU test execution.
    When this PR is combined with PR ci: run CPU validation on hosted runners #13, the CPU job fails during collection because test_env_step_none_action.py imports isaaclab. Please keep Isaac-only tests out of the hosted CPU collection and run them in a dedicated trusted Isaac/GPU job.

  6. Complete the validation evidence.
    Please run the repository’s pre-commit hooks—Black and isort currently report changes—and provide the successful test logs together with the exact Isaac Lab and Isaac Sim versions used.

Once these items are addressed and both CPU and Isaac runtime checks pass, this should be ready for merge.

Lila Anafi and others added 7 commits September 12, 2026 01:03
action can legitimately be None for a step whose StateMachine action
list has no agent_assets at all (e.g. a pure SemanticActionCfg step
like TurnOnHeaterCfg). matterix_sm's "hold current pose" fallback in
StateMachine.step() only initializes for agents referenced in that
action sequence, not the scene's full action space, so it stays None
when a sequence is composed entirely of semantic-only actions -
crashing on action.to(self.device).

Mirror how semantic_actions=None already means "apply nothing" for the
sibling parameter: skip processing rather than crash.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Addresses reviewer request for complete regression coverage of the
action=None guard added in the previous commit.

- Fix the same unconditional action.to(device) crash the reviewer flagged
  in the standard runner (scripts/run_workflow.py) and in
  test_video_recording.py, which both crashed before ever reaching
  MatterixBaseEnv.step()'s guard for a semantic-only workflow.
- Add StateMachine unit tests (source/matterix_sm/test/) covering: a
  pure-semantic sequence returning action=None with exactly one semantic
  action emitted and the sequence marked successful; and a mixed sequence
  where a later robot action still yields a valid hold tensor during the
  initial semantic-only step. matterix_sm has no isaacsim/omni imports, so
  these run as plain pytest with no Isaac Sim launch required.
- Add an Isaac runtime test (source/matterix/test/test_env_step_none_action.py)
  verifying against a live env that action_manager.process_action() is not
  called when action=None, the semantic action is applied exactly once, and
  action_manager.action stays byte-for-byte unchanged across the None step -
  covering both the reset-then-semantic and robot-action-then-semantic
  (hold-previous-target) transitions.
- Add an end-to-end runner regression test
  (source/matterix/test/test_run_workflow_semantic_only.py) plus a new
  "heater_only" pure-semantic workflow on the heat-transfer test task, driving
  scripts/run_workflow.py as a real subprocess through multiple full episodes.
- Document the None contract on both step() signatures: MatterixBaseEnv.step()
  and StateMachine.step() now type-annotate action as optional and explain
  what None means for physical control.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Review feedback: the mixed-workflow test's "later robot action" was
WaitCfg(agent_assets="robot") with no action_space_info, which produces a
(num_envs, 1) hold tensor and never exercised a real action space, while
the Franka environment actually expects 8 dimensions. The test passed
without proving anything representative.

Replace it with MoveRelativeCfg(action_space_info=FRANKA_IK_ACTION_SPACE),
a genuine motion action, and give the stub SceneData a real (if arbitrary)
robot EE pose so it can compute its target. Strengthen the final assertion
to check the full action shape against FRANKA_IK_ACTION_SPACE.total_dim
(2, 8) instead of only checking the batch dimension.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Review feedback:
- action_manager.action only reflects the raw input buffer before per-term
  processing, so it never proved anything about the actual controller/
  actuator target applied to the robot. Added checks against
  env.scene["robot"].data.joint_pos_target - the real PD-controller target
  PhysX applies every decimation substep - for both scenarios.
- "Heater turned on" in obs only proved the semantic was applied at least
  once, not exactly once. Spy on IsHeaterOn.set_value() (the actual
  application point) directly and assert a call count of 1.

The joint_pos_target checks are not byte-for-byte equality: measured
empirically that this Franka arm's IK controller re-solves against the
robot's live, still-converging joint state every step, so the target
naturally drifts by a similar amount (~0.24-0.27 rad max-abs-diff) whether
or not a new action was given. A strict-equality check would fail on
correct behavior. Instead assert what actually indicates a real problem:
the target is not dropped to zero and contains no NaN/Inf.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Review feedback: proc.stdout.readline() blocks indefinitely if the
subprocess goes quiet without producing another line, so the test's stated
90s timeout wasn't actually structurally enforced. Separately, declaring
success on seeing "EPISODE 2" and then force-terminating the process only
proved the first episode completed - not that the run would finish
successfully on its own.

Add --max_episodes to scripts/run_workflow.py so its main loop (which runs
forever by default) can be told to stop and exit normally after N episodes
- a generally useful automation/CI option for the runner, not just test
scaffolding. The test now passes --max_episodes 2 (the minimum needed to
prove the reset-and-repeat cycle works, not just a single run), uses
subprocess.communicate(timeout=90) instead of a manual readline() loop so
the timeout is enforced regardless of output flow, and asserts the process
exited normally with returncode 0 - proving the run actually completed,
rather than "we saw promising output before we killed it."

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Review feedback: when combined with AccelerationConsortium#13 (CPU validation on hosted
runners), the CPU job's `pytest source/matterix_sm/test source/matterix/test`
fails during collection, because test_env_step_none_action.py imports
isaaclab at module level and isaaclab isn't installed in that job.

Add source/matterix/test/conftest.py with collect_ignore for
test_env_step_none_action.py and test_run_workflow_semantic_only.py -
neither is a real pytest test module (no def test_* functions; they're
standalone scripts meant to be run directly via AppLauncher, or to spawn
one in a subprocess), so excluding them from collection changes nothing
about how they're actually run, just stops pytest from trying to import
them somewhere that can't support that.

Verified: reproduced the exact CPU-job failure in a from-scratch venv
matching AccelerationConsortium#13's install recipe (numpy, pytest, torch cpu, matterix_sm
editable only, no isaaclab) - collection errors without this conftest.py,
11 tests pass cleanly with it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Review feedback: Black and isort reported changes on this branch's files.
Ran the full pre-commit hook suite (black, flake8, isort, pyupgrade,
codespell, and the misc pre-commit-hooks checks) scoped to every file this
branch touches - all pass clean now. Purely cosmetic: line-wrapping and
import ordering, no behavior changes. Re-verified all three live Isaac Sim
tests and the pure-Python StateMachine tests still pass after reformatting.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@lanafi00
lanafi00 force-pushed the fix/action-manager-none-action branch from 3e0c024 to b83659b Compare September 12, 2026 05:13
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