Skip to content

Add robot profile: agibot/x2/agibot.x2.mujoco-drake-push.v1 - #107

Open
hossein6191 wants to merge 3 commits into
fabricfoundation:agibot-x2-tier-1from
hossein6191:agibot-x2-tier-1-push
Open

Add robot profile: agibot/x2/agibot.x2.mujoco-drake-push.v1#107
hossein6191 wants to merge 3 commits into
fabricfoundation:agibot-x2-tier-1from
hossein6191:agibot-x2-tier-1-push

Conversation

@hossein6191

Copy link
Copy Markdown

AgiBot X2 · Tier 1 — simulator-only. No physical robot.

A payer names where a puck is and where it should end up. Payment is verified
before anything moves, and a failed action never settles.

  • registry/vendors/agibot/x2/agibot.x2.mujoco-drake-push.v1/ — profile package
  • bridge/agibot/x2/ — Zenoh bridge, x402 gate, IK planner, both engines

Not a replayed animation

Both ends of the motion are parameters of the paid action. Every waypoint is
solved at run time by a constrained IK solver against the puck's measured
pose, and the push direction comes from the live puck-to-goal vector. Stage
transitions fire on sensed conditions; timers exist only as failure guards.

Measured results

Reproducible with python -m sim_bridge.tools.collect_evidence --sim2sim-cases 10 --json.
Targets are sampled from the advertised envelope with a fixed seed, so the
numbers are not hand-picked.

Targets delivered 10 / 10
Sim-to-sim verdicts matching 10 / 10
Worst inter-engine disagreement 51 mm (tolerance 100 mm)
Payment gate cases 13 / 13 as specified
Settlement on failure never
Tests 48

Method, full numbers and the raw evidence: docs/validation-report.md and
docs/evidence/.

Runs against the tunnel's real wire format

This is worth flagging because it was wrong first. The tunnel in this
repository does not publish a flat action envelope: POST /action sits behind
the x402 middleware and handlers.go publishes

{payload, transaction_details, timestamp}

to robot/tunnel/action — the client's body wrapped verbatim, with the
resolved x402 payload beside it. Two defects followed, and neither could have
been caught by tests that spoke the same invented dialect as the bridge:

  1. Only the flat envelope parsed, so every message the real tunnel sends
    would have been refused as malformed — an integration that passes its own
    tests and works with nothing.
  2. A transaction hash was required before the robot would act. x402
    verifies, runs the resource handler, then settles — skipping settlement
    when the handler fails. No hash exists when the robot is asked to move.
    Demanding one rejected every real message, and inverted the exact lifecycle
    that gives no-settle-on-failure its meaning.

Both shapes now go through one parser. On arrival the bridge requires a
verified payment plus an authorizationRef — a digest of the x402
authorisation the tunnel verified — rather than a settlement reference that
cannot exist yet.

Checked against the tunnel itself, not just its source.
tunnel/cmd/tunnelprobe drives the real handlers.PostAction through the real
zenoh.Session publisher, with the two context values the x402 gin middleware
sets on a verified payment:

probe bridge verdict
default (payment verified) act_probe_307824656000 SUCCESS, settle=true
-unpaid act_probe_323940412000 PAYMENT_REQUIRED, settle=false

The bytes that crossed the wire are committed as
docs/evidence/tunnel-wire-capture.json. Two things in them are why the fix
was needed: the action fields sit wrapped under payload, and there is no
tx_hash anywhere in the message.

This also closes a hole the wrapper opens. The tunnel forwards the request body
verbatim, so a caller reaching the topic can put
payment: {verified: true, txHash: ...} inside it. Verification is now read
only from transaction_details. Measured: a body claiming a verified payment
of 999999 with a forged hash is refused with PAYMENT_REQUIRED.

Model defects found and fixed

Three things had to be corrected before the two engines could be compared
honestly, each of which otherwise produced something that looked like a physics
result and was not. Chief among them: the tool point was the wrist frame, not
the hand. The hand is a slab ~200 mm deep hanging below that frame, so
planning to it aimed 100 mm under every waypoint — MuJoCo still caught the puck
with the top edge of its larger collision mesh and appeared to work, while
Drake's smaller box passed underneath and never touched it. The sim-to-sim
disagreement was not physics; it was one engine's geometry accidentally
covering a planning bug.

The arm is also not uniform: shoulder and elbow carry 24–36 Nm while the two
wrist trim joints carry 2.2 Nm and stall against any contact, so the planner is
not allowed to spend them.

What is not claimed

Two links in the chain remain unexercised, and neither can be closed from this
repository alone:

  • No payment was actually settled. The probe supplies a verified-payment
    context rather than driving the x402 facilitator, because genuine
    verification needs a funded Base Sepolia key — which belongs to the operator,
    not to a repository.
  • The tunnel's WebSocket link to the Fabric proxy was not used. The tunnel
    serves its router through internal.NewClient(cfg.ProxyWSURL, ...) rather
    than binding a local port, and the proxy is not part of this repository.

What is established is everything between the tunnel's HTTP handler and the
robot's verdict: the real handler's bytes, the bridge's parse, the execution,
and the settlement decision.

There is no ROS2 here; the profile states ros2: null rather than listing a
topic nothing publishes.

hossein6191 and others added 3 commits August 17, 2026 17:36
Adds a RoboPay integration for the AgiBot X2: a payer names where a puck
is and where it should end up, payment is verified before anything moves,
and a failed action never settles.

  bridge/agibot/x2/    Zenoh bridge, x402 gate, IK planner, both engines
  registry/vendors/agibot/x2/agibot.x2.mujoco-drake-push.v1/

Not a replayed animation: both ends of the motion are parameters of the
paid action, every waypoint is solved at run time against the puck's
measured pose, and stage transitions fire on sensed conditions.

Measured results (registry .../docs/validation-report.md, reproducible via
sim_bridge.tools.collect_evidence):

  10/10 targets delivered, sampled from the advertised envelope
  10/10 sim-to-sim verdicts match, worst gap 51mm against 100mm tolerance
  9/9 payment gate rules behave as specified; settle=true only on success

Three model-level defects had to be fixed before the two engines could be
compared honestly, each of which otherwise produced something that looked
like a physics result and was not:

  * The tool point was the wrist frame, not the hand. The hand is a slab
    ~200mm deep hanging below that frame, so planning to it aimed 100mm
    below every waypoint. MuJoCo still caught the puck with the top edge
    of its larger collision mesh and appeared to work; Drake's smaller box
    passed underneath and never touched it.
  * Six links carry visual-only geometry in AgiBot's MuJoCo scene while
    the URDF gives them collision tags. Drake's hull for one of them
    struck the puck at 99.8N mid-raise. Drake now matches the vendor's own
    collision set.
  * The Drake table was a thin slab where MuJoCo's is solid, and the puck
    tunnelled through the middle of it.

The IK plans with five joints, not seven: shoulder and elbow carry 24-36Nm
while the two wrist trim joints carry 2.2Nm and stall against any contact,
so a plan that spends them is not executable.

The advertised envelope is narrow and measured rather than assumed --
swept through both engines, keeping only what both deliver. Requests
outside it are refused rather than attempted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The bridge was written against a flat action envelope that nothing in this
repository publishes. Reading tunnel/internal/handlers/handlers.go, `POST
/action` sits behind the x402 middleware and the handler publishes

    {payload, transaction_details, timestamp}

to robot/tunnel/action -- the client's body wrapped verbatim, with the
resolved x402 payload and requirements beside it. Two defects followed, and
neither could be caught by tests that spoke the same invented dialect as the
bridge:

  * Only the flat envelope parsed, so every message the real tunnel sends
    would have been refused as malformed. An integration that passes its own
    tests and works with nothing.

  * A transaction hash was required before the robot would act. x402 verifies,
    runs the resource handler, and settles afterwards -- skipping settlement
    when the handler fails (x402/server.go calls the cancellation path "after
    a successful Verify but before/instead of Settle when the resource handler
    errors"). No hash exists when the robot is asked to move. Demanding one
    rejected every real message, and inverted the exact lifecycle that gives
    no-settle-on-failure its meaning.

Both shapes now go through one parser. On arrival the bridge requires a
verified payment plus an authorizationRef -- a digest of the x402
authorisation the tunnel verified -- rather than a settlement reference that
cannot exist yet.

Also closes a hole the wrapper opens: the tunnel forwards the request body
verbatim, so a caller can put `payment: {verified: true}` in it. Verification
is now read only from transaction_details, never from the body. Measured, a
body claiming a verified payment of 999999 with a forged hash is refused with
PAYMENT_REQUIRED.

  48 tests (6 new, built from the Go source and the x402 v2 types)
  13 payment gate cases measured, 4 of them through the real wrapper
  send_action --tunnel-format publishes the true shape on demand

Not claimed: this has not been run against the Go tunnel binary. Go is not
installed here, and an end-to-end run also needs a funded Base Sepolia key,
which belongs to the operator rather than to this repository. What is verified
is that the bridge accepts, and correctly refuses, the exact bytes handlers.go
constructs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous commit derived the tunnel's message shape by reading
handlers.go. This runs the tunnel's own code and compares.

tunnel/cmd/tunnelprobe drives the real handlers.PostAction through the real
zenoh.Session publisher, with the two context values the x402 gin middleware
sets on a verified payment. The handler, its JSON shaping and its publisher
are untouched; what is not run is the middleware itself, which needs a
facilitator and a funded key.

With the bridge subscribed:

  default    -> act_probe_307824656000  SUCCESS, settle=true
  -unpaid    -> act_probe_323940412000  PAYMENT_REQUIRED, settle=false

The bytes that crossed the wire are committed as
evidence/tunnel-wire-capture.json, and they match the reproduction in
send_action --tunnel-format field for field. Two of them are the reason the
previous commit existed: the action sits wrapped under `payload`, and there is
no tx_hash anywhere in the message -- so the settlement reference this bridge
used to require does not exist at that point in the protocol, and demanding it
rejected every real message.

The validation report now states what remains unexercised rather than implying
the chain is complete: no payment was actually settled, and the tunnel's
WebSocket link to the Fabric proxy was not used. Neither can be closed from
this repository alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hossein6191
hossein6191 requested a review from a team August 17, 2026 10:18
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.

1 participant