# Placing Held Feature

> Move a held object so one of its declared features lands on a target point — a ring onto a hook, a tip into a hole, a plug at a bore. Composes the hand pose from the live object pose and the feature's centre in the object's own frame, recomputed each round so in-jaw slip cannot compound. Use for any goal written as "this object's feature at that place"; do NOT drive the hand to the target directly, which is wrong by the length of the held object.

- Skill: `graph-robots/placing-held-feature` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add graph-robots/placing-held-feature`
- Raw SKILL.md: https://api.skillmd.com/api/skills/graph-robots/placing-held-feature/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: graph-robots (https://skillmd.com/u/graph-robots)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/graph-robots/placing-held-feature

---


# placing-held-feature

The **place** stage's geometry: where must the hand be so that the thing it is
holding arrives where the goal wants it.

## Why this is its own skill

Grasping has a whole ladder of strategies and placing had none, which is
backwards for any goal phrased as a relation between a held object's feature
and a fixture. The composition is small and the mistake is not:

```
feature_now = object_position + R(object_rotation) · feature_local
move        = target − feature_now
hand_goal   = hand_now + move
```

Without it the obvious move is to drive the hand at the target, and that is
wrong by the length of the tool. It fails as a refusal rather than as a miss,
so it reads like an unreachable workspace rather than a mis-specified goal —
a diagnosis that costs iterations, because the fix it suggests (try the other
arm, give up on the target) is not the fix.

## When to use

- A goal written as *object.feature* at *fixture.feature* or in a region:
  a loop over a rod, a shaft into a hole, a plug at a bore.
- Any time the held object is large relative to the hand.

## When NOT to use

- To orient. This translates only; turn first, then mate.
- Before the object is held. It reads the object's live pose, which before a
  grasp is wherever it is lying.

## Recommended subgraph state flow

```text
locate → orient → mate → plan → execute → verify
```

1. **`locate`** — the target point. For a welded fixture with no body and no
   OBB, that is `sim.query` on the goal's own atom and refs; its `at` is the
   feature's world position.
2. **`orient`** — turn the held object so the mating axis lines up, as a locked
   `motion.plan_joint` at the current position. Transit first, then turn.
3. **`mate`** — `script: scripts/<sg>/mate_feature.py`. Route on `route`.
4. **`plan`/`execute`** — `motion.plan_linear(end=Ref("mate.hand_goal"),
   orientation="lock")` then `robot.execute_trajectory`. Locked, because the
   orientation you turned to is the one that must survive the move.
5. **`verify`** — re-run `mate` and read `distance_m`. Shrinking means the
   round worked; flat means the move is not being executed as planned.

Use `stop_short_axis`/`stop_short_m` when the fixture is mounted flush to a
board: close the two free axes fully and stop short on the board's normal, so
the approach does not drive the load into the mounting.

## Required end states

| End state | Meaning |
|---|---|
| `ready` | `hand_goal`, `move_m`, `feature_now`, `distance_m`. |
| `unknown` | A pose or point could not be read. Nothing moved; fix the inputs. |

