Add robot profile: agibot/x2/agibot.x2.mujoco-drake-push.v1 - #107
Open
hossein6191 wants to merge 3 commits into
Open
Add robot profile: agibot/x2/agibot.x2.mujoco-drake-push.v1#107hossein6191 wants to merge 3 commits into
hossein6191 wants to merge 3 commits into
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 packagebridge/agibot/x2/— Zenoh bridge, x402 gate, IK planner, both enginesNot 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.
Method, full numbers and the raw evidence:
docs/validation-report.mdanddocs/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 /actionsits behindthe x402 middleware and
handlers.gopublishesto
robot/tunnel/action— the client's body wrapped verbatim, with theresolved x402 payload beside it. Two defects followed, and neither could have
been caught by tests that spoke the same invented dialect as the bridge:
would have been refused as malformed — an integration that passes its own
tests and works with nothing.
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 x402authorisation the tunnel verified — rather than a settlement reference that
cannot exist yet.
Checked against the tunnel itself, not just its source.
tunnel/cmd/tunnelprobedrives the realhandlers.PostActionthrough the realzenoh.Sessionpublisher, with the two context values the x402 gin middlewaresets on a verified payment:
act_probe_307824656000SUCCESS, settle=true-unpaidact_probe_323940412000PAYMENT_REQUIRED, settle=falseThe bytes that crossed the wire are committed as
docs/evidence/tunnel-wire-capture.json. Two things in them are why the fixwas needed: the action fields sit wrapped under
payload, and there is notx_hashanywhere 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 readonly from
transaction_details. Measured: a body claiming a verified paymentof 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:
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.
serves its router through
internal.NewClient(cfg.ProxyWSURL, ...)ratherthan 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: nullrather than listing atopic nothing publishes.