# The transfer gap

Measure what an embodiment change costs before your customer does, then close the gap cluster by cluster.

A policy that scores well on the embodiment it was trained on has told you
nothing about the one your customer runs.
[`transfer()`](/docs/primitives/transfer/) measures the drop directly — same
task, same perturbation schedule, source embodiment against target — so the
gap is a number with an interval, not a hunch.

*Example: measure the gap*

**Python**

```python
import roborama  # reads ROBORAMA_API_KEY from the environment

policy = roborama.Policy.checkpoint(hf="acme/skill-v4", runtime="openpi")

gap = roborama.transfer(
    policy=policy,
    source="aloha-bimanual@fw3.1",   # the embodiment it was trained on
    target="g1-edu-pro@fw2.3",
    task="fold_towel@v2",
)

print(gap.report())
# source: n=380  rate=0.942  ci95=(0.914, 0.961)
# target: n=340  rate=0.715  ci95=(0.665, 0.760)
# failure clusters opened in transfer: [grasp_slip, wrist_singularity]
```

**TypeScript**

```typescript
import Roborama from "@roborama/sdk"; // reads ROBORAMA_API_KEY

const roborama = new Roborama();

const gap = await roborama.runs.create({
  kind: "transfer",
  policy: { type: "checkpoint", hf: "acme/skill-v4", runtime: "openpi" },
  source: "aloha-bimanual@fw3.1", // the embodiment it was trained on
  target: "g1-edu-pro@fw2.3",
  task: "fold_towel@v2",
});

const report = await gap.report();
console.log(report);
// source: n=380  rate=0.942  ci95=(0.914, 0.961)
// target: n=340  rate=0.715  ci95=(0.665, 0.760)
```

**cURL**

```bash
curl https://api.roborama.com/v1/runs \
  -H "Authorization: Bearer $ROBORAMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "transfer",
    "policy": { "type": "checkpoint", "hf": "acme/skill-v4",
                "runtime": "openpi" },
    "source": "aloha-bimanual@fw3.1",
    "target": "g1-edu-pro@fw2.3",
    "task": "fold_towel@v2"
  }'
```

**Agent (tool-use payload)**

```json
{
  "type": "tool_use",
  "name": "roborama_transfer",
  "input": {
    "policy": { "type": "checkpoint", "hf": "acme/skill-v4",
                "runtime": "openpi" },
    "source": "aloha-bimanual@fw3.1",
    "target": "g1-edu-pro@fw2.3",
    "task": "fold_towel@v2"
  }
}
```

## Reading the report

On `fold_towel@v2`, the canonical policy scores 0.942 (n=380, ci95
0.914–0.961) on `aloha-bimanual@fw3.1`, the embodiment it was trained on. On
`g1-edu-pro@fw2.3` it scores 0.715 (n=340, ci95 0.665–0.760) — a 22.7-point
gap, and the intervals are nowhere near overlapping. This is not noise.

The number says how much. The clusters say what: `grasp_slip` and
`wrist_singularity` opened in transfer — failure modes the source embodiment
never exhibited. A different gripper geometry slips where the source's
didn't; the target's wrist reaches joint configurations the policy never had
to steer around. That is a diagnosis, not just a grade, and it is where the
playbook starts.

## The playbook

### Read the clusters first

They partition the gap into causes you can act on. A gap that is mostly
`grasp_slip` is usually a data problem; `wrist_singularity` points at a
contract or retargeting problem.

### Pull the failing episodes

Filter episode artifacts by cluster and read the evidence: video for the
what, MCAP for the why — `/joint_states_measured` against
`/joint_targets_commanded` shows exactly where execution diverged from
intent.

### Fix what broke

Fine-tune on target-embodiment episodes (`run.export(format="lerobot")` feeds
most training stacks), or fix the declared contract — an `action_space` or
`observation_contract` mismatch produces exactly these clusters. See
[policies](/docs/concepts/policies/).

### Re-measure

Re-run `transfer()` with the same pins. For before/after fine-tune deltas,
[`compare()`](/docs/primitives/compare/) with `paired=True` runs both
checkpoints against the same initial conditions per episode pair — pairing
halves the n you need to separate them.

### Pin it so it can't reopen

Once the gap closes, add the target embodiment as a row in your
[`matrix()`](/docs/primitives/matrix/) and to a
[CI gate](/docs/guides/ci-gates/). A transfer gap you closed once is a
regression waiting for a firmware bump.

Measure before your customer does. The gap does not care which of you finds
it first, but your release process should.
