User Guide · Core Concepts · Atomic skills

Atomic skills

MetaFine ships 21 motion-planning primitives, each typed by the affordances it requires and the phase it belongs to. New skills are registered with a single decorator and become composable immediately.

What is a skill?

A skill is the smallest unit of interaction in MetaFine — a motion-planning solver that achieves one well-defined operation on one well-defined part of an articulated asset, with a clear pre/post contact-state contract. Grasp this handle. Rotate this knob 90°. Slide this drawer 5 cm out. Skills are not learned policies; they are deterministic experts used to generate demonstrations and to define what a task even means.

Every skill carries metadata: which affordances it requires of its target part, which phase it belongs to, and default parameters that make it run without arguments.

The three phases

Skills are classified by what they do to the contact state — this classification determines what composes safely with what.

Interaction
Engages the object; transitions contact state. Starts a chain.
grasp_part · press · flip_switch · lift_lid · insert · place
Continuation
Operates on an already-engaged part. Safe to chain after interaction.
pure_rotate · pure_slide · pure_insert · pure_lift · release_gripper
Bundle
Pre-composed multi-step routines kept atomic for now (until they're split).
lid_opening · stand_up · toggle_switch

The phase distinction is enforced by the task-graph validator: a continuation skill emits a clear error if the preceding stage left the gripper disengaged.

Skill catalogue

SkillPhaseRequires (affordance)Typical use
grasp_partinteractiongraspableEngage a named part with the gripper.
pressinteractionpressablePress a button / pad without grasping.
flip_switchinteractionflippableToggle a switch (single binary motion).
lift_lidinteractionliftableGrasp + lift a removable lid.
insertinteractioninsertableAlign + insert a part into a socket / hole.
placeinteractionplaceableSet a held object onto a surface.
stackinteractionstackablePlace onto another object.
pure_rotatecontinuationrotatableRotate the already-grasped part about its joint axis.
pure_slidecontinuationslidableTranslate the already-grasped part along its prismatic joint.
pure_liftcontinuationliftableRaise the held object by N cm.
pure_insertcontinuationinsertableContinue insertion from a pre-aligned pose.
release_grippercontinuationOpen the gripper; terminate the engagement.
draw_partinteractiondrawablePull a drawer / sliding part.
open_partinteractionopenableOpen a door / hinged panel.
lid_openingbundlegraspable + liftableBundle: grasp lid then lift to clearance.
stand_upbundlegraspable + liftableBundle: tip a lying object upright.
toggle_switchbundleflippableBundle: approach + flip + retract.

(A handful of internal variants are omitted; consult SKILL_REGISTRY for the live list.)

The skill registry

core.skill_registry.SKILL_REGISTRY is the single source of truth that maps skill name → SkillSpec (the solver callable, the required affordances, the phase, default parameters). At import time every skill module registers itself; downstream code looks skills up rather than hardcoding strings.

from core.skill_registry import SKILL_REGISTRY

spec = SKILL_REGISTRY["pure_rotate"]
print(spec.phase, spec.requires)
# → 'continuation' ('rotatable',)

@register_skill

A new skill is a decorator + a function. The decorator declares its phase + affordance contract:

from core.skill_registry import register_skill

@register_skill(
    phase="interaction",
    requires=("graspable", "pressable"),
    defaults={"force_n": 8.0},
)
def grasp_and_press(env, target, *, force_n=8.0):
    """Engage the part and apply a brief press."""
    grasp_part(env, target)
    press(env, target, force_n=force_n)
    return env.last_step_info

Once imported, the new skill is automatically eligible in task graphs whose target parts declare both graspable and pressable.

Composing skills

Two ways to compose:

  • YAML task graphs — declarative; preferred for evaluation. See Task graphs.
  • Python callbacks — for programmatic curricula or learned planners. Call run_task_graph(env, graph) directly from your script.

Both paths share the same validator, so an invalid chain (continuation after disengagement, missing affordance on the target) fails the same way regardless of how it was constructed.