Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
3d1827f
Reorganize interactive simulation documentation (#508)
yuecideng Aug 12, 2026
e01b457
Document Codex and Claude Code contribution support (#512)
yuecideng Aug 13, 2026
0739b59
Add Franka drawer manipulation tutorial (#513)
yuecideng Aug 13, 2026
b3ed4c5
Fix Viser rendering for simulation axis markers (#511)
yuecideng Aug 14, 2026
0cee4c6
Fix croodinate pickment (#514)
matafela Aug 14, 2026
496b860
Simplify atomic action planning contracts (#517)
yuecideng Aug 18, 2026
18fd07e
feat(sim): add declarative scene and robot skill foundation (#487)
yuecideng Aug 18, 2026
f4ffb66
Remove private index from cuRobo install commands (#518)
yuecideng Aug 18, 2026
3e7875f
Support Franka in atomic-action tutorials (#519)
yuecideng Aug 19, 2026
56dbe01
Simplify declarative skill foundation code (#523)
yuecideng Aug 19, 2026
dbc6553
docs: support NVIDIA 595 and refresh documentation (#524)
yuecideng Aug 19, 2026
8df210c
add articulation affordance (#509)
matafela Aug 19, 2026
fd9e663
Add codebase navigation to project context skill (#526)
yuecideng Aug 19, 2026
b77603c
Require explicit timing for atomic action trajectories (#525)
yuecideng Aug 19, 2026
e8b9688
refactor(atomic-actions): generalize runtime endpoints (#488)
yuecideng Aug 20, 2026
12178b3
Improve log output and environment initialization summary (#527)
yuecideng Aug 20, 2026
02151ce
Fix interrupted tutorial shutdown (#529)
wu-simulab Aug 20, 2026
c39a27a
Add public API documentation coverage workflow (#530)
yuecideng Aug 21, 2026
0475efa
feat(sim): add semantic skill IR and compiler
skywhite1024 Aug 21, 2026
7b8424c
fix(skills): inherit held resource selections
skywhite1024 Aug 21, 2026
867d7a2
refactor(atomic-actions): preserve per-environment runtime lifecycle
yuecideng Aug 10, 2026
84928a0
refactor(atomic-actions): verify effects on due observations
yuecideng Aug 10, 2026
0968684
feat(atomic-actions): complete verified action runtime
yuecideng Aug 11, 2026
370e5f8
feat(sim): add semantic runtime effects and parallelism
yuecideng Aug 11, 2026
50c1395
feat(gym): add declarative expert program runtime
yuecideng Aug 11, 2026
d6be5c1
refactor(expert-program): remove handover receiver alias
skywhite1024 Aug 21, 2026
826454d
feat(agents): add strict expert program frontend
yuecideng Aug 11, 2026
8bf6a50
test(agents): reject removed handover receiver alias
skywhite1024 Aug 21, 2026
eeb6f83
feat(tasks): add declarative expert program vertical slices
yuecideng Aug 11, 2026
872b8a1
feat(benchmark): add expert program rollout validation
yuecideng Aug 11, 2026
57e47f3
refactor(atomic-actions): add typed tracking contracts
yuecideng Aug 11, 2026
de750cc
feat(expert-program): add task-owned pre-sim catalogs
yuecideng Aug 11, 2026
ca0ef14
fix(expert-program): reject opaque catalog values
yuecideng Aug 11, 2026
8b8ad58
feat(expert-program): configure semantic action options
yuecideng Aug 11, 2026
33c56c0
feat(expert-program): own standard runtime extensions
yuecideng Aug 11, 2026
e7214d7
fix(solvers): construct configs with final parameters
yuecideng Aug 11, 2026
895f063
feat(tasks): add declarative physical hand-over
yuecideng Aug 11, 2026
1ca4170
feat(atomic-actions): guard in-flight held objects
yuecideng Aug 11, 2026
c13a5d4
feat(skills): expose per-expectation effect outcomes
yuecideng Aug 11, 2026
d857250
feat(atomic-actions): reconcile terminal effect failures
yuecideng Aug 11, 2026
31104ae
feat(skills): gate motion on physical effects
yuecideng Aug 11, 2026
fc294f7
feat(skills): add bounded workflow reacquisition
yuecideng Aug 11, 2026
9c5d12f
test(skills): use explicit handover resource slots
skywhite1024 Aug 21, 2026
420cad6
feat(skills): add declarative placement relations
yuecideng Aug 11, 2026
ba13943
feat(expert-program): validate parallel joint segments
yuecideng Aug 11, 2026
30dd2d0
test(tasks): gate cube physical recovery
yuecideng Aug 11, 2026
ac03e82
feat(atomic-actions): improve upright grasp selection
skywhite1024 Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
149 changes: 125 additions & 24 deletions .agents/skills/add-atomic-action/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@ Inspect only the files relevant to the requested skill:
|---|---|
| Base action and descriptors | `embodichain/lab/sim/atomic_actions/core.py` |
| Goals and dynamic pose references | `embodichain/lab/sim/atomic_actions/goals.py` |
| Role-to-resource binding | `embodichain/lab/sim/atomic_actions/bindings.py` |
| Skill endpoint requirements | `embodichain/lab/sim/atomic_actions/requirements.py` |
| Resolved endpoint bindings and targets | `embodichain/lab/sim/atomic_actions/bindings.py` |
| Invocation, options, and resolved request | `embodichain/lab/sim/atomic_actions/invocation.py` |
| Control-part semantic commands | `embodichain/lab/sim/atomic_actions/control.py` |
| Invocation policies | `embodichain/lab/sim/atomic_actions/policies.py` |
| Robot/task/scene state | `embodichain/lab/sim/atomic_actions/state.py` |
| Dynamic scene provider contract | `embodichain/lab/sim/atomic_actions/scene.py` |
| Effects and plans | `embodichain/lab/sim/atomic_actions/effects.py`, `plans.py` |
| Runtime command frames and payloads | `embodichain/lab/sim/atomic_actions/runtime_commands.py` |
| Endpoint command transports | `embodichain/lab/sim/atomic_actions/transports.py` |
| Trajectory helpers | `embodichain/lab/sim/atomic_actions/trajectory_ops.py` |
| Engine-owned planning resources | `embodichain/lab/sim/atomic_actions/runtime.py` |
| Declarative robot resources and adapters | `embodichain/lab/sim/skills/profiles.py` |
| Reference implementations | `embodichain/lab/sim/atomic_actions/primitives/` |
| Static compiler and execution session | `engine.py`, `execution.py` |
| Controller-facing execution ports | `runner.py`, `sim_adapter.py` |
Expand Down Expand Up @@ -92,24 +96,33 @@ class PushOptions(ActionOptions):
push_distance: float = 0.05
```

Do not put arm/hand names, hand qpos, or named robot postures in options. Bind
participants with `ActionBinding`. Register embodiment-specific commands such
as `open`, `grasp`, or `ready` on `ControlPartCommandProfile`; use
Do not put arm/hand names, hand qpos, or named robot postures in options.
Declare robot-independent participant slots and endpoints with
`SkillBindingContract`; the engine or a bound robot skill profile produces the
engine-owned `ActionBinding`. Register embodiment-specific commands such as
`open`, `grasp`, or `ready` on `ControlPartCommandProfile`; use
`ActionControlOverrides` only for one invocation revision.

## 3. Implement the planner

Inherit `AtomicAction[PushGoal, PushOptions]` directly. Declare stable metadata and resolve
resources from semantic binding roles.
Inherit `AtomicAction[PushGoal, PushOptions]` directly. Declare stable metadata
and an explicit, robot-independent endpoint contract. Every concrete action
class must declare `binding_contract` in its own class body; use
`SkillBindingContract()` for a skill that consumes no robot resource.

```python
from typing import ClassVar

from embodichain.lab.sim.atomic_actions import (
ResolvedActionRequest,
ActionPlan,
AtomicAction,
CARTESIAN_POSE_CAPABILITY,
JointPositionTarget,
PlanningContext,
ResolvedActionRequest,
SkillBindingContract,
SkillEndpointRequirement,
SkillResourceSlot,
StateDelta,
)
from embodichain.lab.sim.atomic_actions.trajectory_ops import (
Expand All @@ -122,7 +135,19 @@ class Push(AtomicAction[PushGoal, PushOptions]):
skill_id: ClassVar[str] = "push"
GoalType: ClassVar[type] = PushGoal
OptionsType: ClassVar[type] = PushOptions
manipulator_roles: ClassVar[tuple[str, ...]] = ("primary",)
binding_contract: ClassVar[SkillBindingContract] = SkillBindingContract(
slots=(
SkillResourceSlot(
slot_id="primary",
endpoints=(
SkillEndpointRequirement(
endpoint_id="motion",
capabilities=frozenset({CARTESIAN_POSE_CAPABILITY}),
),
),
),
),
)

def __init__(self, default_options: PushOptions | None = None) -> None:
super().__init__(default_options)
Expand All @@ -133,11 +158,13 @@ class Push(AtomicAction[PushGoal, PushOptions]):
context: PlanningContext,
) -> ActionPlan:
goal = self.require_goal(request)
options = request.skill_options
manipulator = request.binding.manipulator("primary")
control_part = manipulator.name
joint_ids = list(manipulator.joint_ids)
motion_target = request.binding.endpoint(
"primary", "motion"
).require_target(JointPositionTarget)
control_part = motion_target.control_part
joint_ids = list(motion_target.joint_ids)
start_qpos = context.robot.qpos[:, joint_ids]
target_poses = goal.contact_pose

# Build planner states and generate controlled-joint motion using
# request.motion_policy. Embed it into full robot DoF.
Expand Down Expand Up @@ -168,6 +195,11 @@ Follow these invariants:

- Let the engine supply `self.robot` and `self.motion_generator`; use
`_on_bind()` only for robot/device-dependent setup.
- Keep slot and endpoint IDs semantic and robot-independent. Declare all-of
capabilities, required typed commands, and disjointness constraints in the
`SkillBindingContract`; do not infer resources from endpoint names.
- Resolve an endpoint with `request.binding.endpoint(slot_id, endpoint_id)` and
call `require_target(ExpectedTarget)` before using target-specific fields.
- Import pure target-shaping, interpolation, pose-translation, and full-robot
embedding helpers directly from `atomic_actions.trajectory_ops`; keep
stateful planning inside `MotionGenerator`.
Expand All @@ -176,8 +208,8 @@ Follow these invariants:
`plan()` method; the latter injects the latest dynamic obstacle poses into a
copied planner policy.
- Plan from `context.robot.qpos`, never an implicit live robot start state.
- Return full-robot `(B, N, robot.dof)` motion as a tensor or
`TimedTrajectory` with matching `env_ids`.
- For joint-backed motion, return full-robot `(B, N, robot.dof)` motion as a
tensor or `TimedTrajectory` with matching `env_ids` through `build_plan()`.
- Preserve row-local planner success. `build_plan()` normalizes the mask and
replaces unsuccessful trajectory rows with the context's observed qpos.
- Preserve backend timing/derivatives when available.
Expand All @@ -196,7 +228,60 @@ Follow these invariants:
`collision_entity_ids`; supported planners receive those entity poses through
the framework-owned `plan()` entry point.

## 4. Register and invoke
## 4. Emit generic runtime commands when needed

Use `build_command_plan()` when a skill targets a mobile base, whole-body
controller, tool, or another non-joint transport. Build immutable endpoint
commands; keep live controller and device handles in the transport:

```python
target = request.binding.endpoint("primary", "tool").require_target(ToolTarget)
frames = tuple(
RuntimeCommandFrame(
commands=(EndpointCommand(target=target, payload=ToolPayload(value)),),
active_mask=torch.ones(
context.batch_size,
dtype=torch.bool,
device=context.robot.qpos.device,
),
env_ids=context.env_ids,
hold_duration=torch.full(
(context.batch_size,),
request.motion_policy.control_dt,
device=context.robot.qpos.device,
),
)
for value in command_values
)
return self.build_command_plan(
request,
context,
success=success,
commands=TimedCommandSequence(frames=frames, env_ids=context.env_ids),
)
```

For a new transport kind:

1. Define an immutable `RuntimeEndpointTarget` and `RuntimeCommandPayload` with
the same stable `transport_id`; both must return independently owned
snapshots. Payloads also expose `batch_size` and `device`. If target-specific
addressing or safe hold depends on fields beyond the exact target type,
`transport_id`, and `target_id`, override `address_fingerprint` to include
those immutable fields; frames, replans, and revisions preserve it.
2. If declarative robot profiles select it, define a `ResourceEndpoint` and an
exact-type `ResourceEndpointAdapter` that returns `EndpointResolution` with
the runtime target and physical claim metadata.
3. Implement `EndpointCommandTransport.send()`, `hold()`, and `cancel()`, then
register it in `EndpointCommandRouter` used as the `ExecutionRunner` command
sink. The router validates payload types before dispatch.

The default command-plan feedback mode is timed and `joint_trajectory` is
optional. Use joint-position feedback only when a matching full-robot
`joint_trajectory` is supplied. Test target/payload snapshot ownership, frame
batch/device consistency, routing, acknowledgement, hold, and cancel behavior.

## 5. Register and invoke

Register an instance by its class-level `skill_id`:

Expand All @@ -213,10 +298,14 @@ register_action(Push)
Construct a grounded invocation explicitly:

```python
binding = engine.bind_control_parts(
"push",
{"primary": {"motion": "left_arm"}},
)
invocation = ActionInvocation(
skill_id="push",
goal=PushGoal(contact_pose),
binding=ActionBinding(manipulators={"primary": "left_arm"}),
binding=binding,
motion_policy=MotionPolicy(sample_count=60),
recovery_policy=RecoveryPolicy(max_replans=2),
)
Expand All @@ -228,26 +317,33 @@ For dynamic scene updates or online error recovery, create a session with
through `ExecutionRunner`. Use non-blocking `runner.step()` in an existing event
loop or `runner.run_until_blocked()` in a simple application.

## 5. Export and document
`engine.bind_control_parts()` is the explicit direct-core path for joint-backed
control parts. When a `RobotSkillProfile` is installed, prefer
`engine.skill_profile.resolve("push", selections).action_binding` so capability,
command, resource-claim, and custom-adapter validation remain declarative.

## 6. Export and document

Export the goal, options, and action from:

1. `embodichain/lab/sim/atomic_actions/primitives/__init__.py`
2. `embodichain/lab/sim/atomic_actions/__init__.py`

Add the stable skill ID, goal, roles, and effect to
Add the stable skill ID, goal, binding slots/endpoints, and effect to
`docs/source/overview/sim/atomic_actions/builtin_actions.md`. Update API docs for
new public classes. Do not create a compatibility re-export module or a closed
built-in-goal union.

## 6. Test behavior
## 7. Test behavior

Add pure pytest tests under `tests/sim/atomic_actions/`. Cover:

- descriptor `skill_id`, `GoalType`, and required roles;
- invalid goal and missing binding rejection;
- descriptor `skill_id`, `GoalType`, and explicit binding contract;
- invalid goal, wrong binding owner, and missing/extra endpoint rejection;
- per-environment planning success/failure masks;
- full-robot trajectory shape, `env_ids`, timing, and failed-row hold behavior;
- generic command target/payload ownership, frame batch/device consistency, and
optional `joint_trajectory` behavior when the skill emits command frames;
- side-effect-free context handling;
- masked `StateDelta` application for task effects;
- `SceneEntityPose` replanning when the action accepts a dynamic goal;
Expand All @@ -264,9 +360,12 @@ then use the `pre-commit-check` skill before committing.
|---|---|
| Inherit another action | Inherit `AtomicAction` directly; compose helpers. |
| Add one generic target with many optional fields | Define a narrow action-owned goal. |
| Put hardware names in the goal | Bind semantic roles through `ActionBinding`. |
| Put arm/hand control-part names in skill options | Use `ActionBinding` as their only source. |
| Bind a joint, link, TCP frame, or arbitrary name | Every binding value must be a key in `RobotCfg.control_parts`. |
| Put hardware names in the goal | Declare semantic slots/endpoints and resolve an engine-owned binding. |
| Put arm/hand control-part names in skill options | Read typed runtime targets from bound endpoints. |
| Declare legacy role tuples on the action | Declare a class-local `SkillBindingContract`. |
| Use role-specific binding accessors | Use `binding.endpoint(...).require_target(...)`. |
| Construct a binding from role dictionaries | Use a bound skill profile, or `engine.bind_control_parts()` for the direct joint path. |
| Pass an arbitrary joint/link/TCP name to the direct path | `bind_control_parts()` values must be keys in `RobotCfg.control_parts`; add an endpoint adapter for another resource kind. |
| Put hand qpos or named robot postures in skill options | Register semantic commands on the concrete control-part profile. |
| Put planner/recovery knobs in skill options | Move them to invocation policies. |
| Pass a motion generator to each action | Pass it once to `AtomicActionEngine`; construct actions from default options only. |
Expand All @@ -277,4 +376,6 @@ then use the `pre-commit-check` skill before committing.
| Mutate held state after planning | Declare a `StateDelta`. |
| Treat `plan_success` as physical success | Verify effects during execution. |
| Step the simulator from the action | Emit plans; connect execution through `ExecutionRunner`. |
| Put live controller handles in targets or payloads | Keep immutable addressing/data in values and own handles in the transport. |
| Force a non-joint endpoint into a fake trajectory | Emit typed frames with `build_command_plan()` and install its transport. |
| Override public `plan()` | Implement `_plan()` so scene binding cannot be bypassed. |
3 changes: 0 additions & 3 deletions .agents/skills/add-task-env/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,6 @@ from .<name> import <CamelCaseName>Env
__all__ = [..., "<CamelCaseName>Env"]
```

Optional compatibility re-export may also be added in
`embodichain/lab/gym/envs/tasks/__init__.py`.

### 4. Create Test Stub

Place at `tests/gym/envs/tasks/test_<name>.py` (or `tests/learning/` for
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/add-test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Tests mirror the source tree under `tests/`:
embodichain/lab/sim/solvers/pytorch_solver.py → tests/sim/solvers/test_pytorch_solver.py
embodichain/lab/gym/envs/managers/rewards.py → tests/gym/envs/managers/test_reward_functors.py
embodichain/toolkits/graspkit/pg_grasp/foo.py → tests/toolkits/test_pg_grasp.py
embodichain/lab/gym/envs/tasks/rl/push_cube.py tests/gym/envs/tasks/test_push_cube.py
embodichain_tasks/embodichain_tasks/rl/push_cube.py tests/gym/envs/tasks/test_push_cube.py
```

Rules:
Expand Down
13 changes: 13 additions & 0 deletions .agents/skills/pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ git add -A
git commit -m "Format code with black"
```

Then run the read-only public API documentation gate used by CI:

```bash
python docs/scripts/check_api_docs.py
```

If it reports missing public exports, invoke `$update-api-docs` to generate or
update the relevant Sphinx entries and descriptions, then rerun the checker.
Keep generation in that specialized skill; do not add placeholder API docs in
the PR workflow merely to make the gate pass.

### 6. Create or Update Branch

For a single PR, create a feature branch if needed:
Expand Down Expand Up @@ -325,6 +336,7 @@ Fixes #<issue-number>

- [x] I have run the `black .` command to format the code base.
- [ ] I have made corresponding changes to the documentation
- [ ] Public API changes are reflected in the API docs (`python docs/scripts/check_api_docs.py`), if applicable
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] Dependencies have been updated, if applicable.
```
Expand All @@ -345,6 +357,7 @@ Fixes #<issue-number>
| `git status` | Check current state |
| `git diff HEAD` | Show changes |
| `black .` | Format code |
| `python docs/scripts/check_api_docs.py` | Check public API documentation coverage |
| `git checkout -b branch-name` | Create branch |
| `git push -u origin branch` | Push to remote |
| `gh pr create` | Create PR |
Expand Down
Loading
Loading