Skip to content

v0.4.0 "adapters" β€” a brain for any small robot

Latest

Choose a tag to compare

@rokbenko rokbenko released this 02 Sep 17:49

quackd now drives four robots, and each one decides for itself what quackd may ask of it. Every robot is an adapter that hands over a manifest β€” this is what I am, this is what I can do β€” and the verbs the model is offered are built from that manifest and nothing else. The thesis does not change: the LLM picks verbs, the robot's own controllers move, quackd enforces the contract.

A Reachy Mini head on the wall spots the ball and judges the kick while a Microduck walks in and kicks it

A head that cannot walk and a duck that cannot judge its own kick, finishing one task under one contract. Simulator, scripted pilots, zero planner LLM calls.

Highlights

  • A verb that is not in the manifest does not exist β€” not in the registry, not in the MCP tool list, not in .duck validation, not in the prompt. Every robot is a RobotAdapter that returns a RobotManifest from connect(), and default_registry() is now just the Microduck's manifest compiled into verbs. Eight core verbs (observe, report_state, stop, say, move, go_to, search_scan, approach_and) exist only on a body whose manifest meets their requirements β€” a camera for observe, the twist intent and mobility for move β€” and declaring one the body cannot support makes the manifest refuse to build. get_frame, walk_to and walk are permanent aliases in one table, so every 0.3 .duck file still runs unchanged (ADR-0017, ADR-0018). Write one in a day: docs/adapters.md, docs/manifest-spec.md.
  • Four adapters, eleven backends, one flag. --robot <adapter>:<backend> on run, validate, serve-mcp, doctor and list-verbs: microduck (sim2d, mock, jsonrpc, websocket), reachy_mini (sim2d, mock, sdk), lerobot (mock, real), rosbridge (mock, ws) β€” one of the eleven is still a stub that raises with a link. Because the manifest decides the vocabulary, it is fifteen verbs on a Microduck, nine on a Reachy Mini head, seven on an SO-101 class arm with a camera and a pick policy, seven on a wheeled base with a camera topic: a head is never offered kick, an arm is never offered move, and move/go_to clamp to the speed limits the manifest names. --transport X still works as --robot microduck:X, warns once per process, and is gone in 0.5. Per-backend honesty table: docs/adapter-status.md.
  • A head and a duck finish one task together. reachy-spots-duck-kicks: a stationary Reachy Mini spots the ball and judges the kick from its own fresh frames, a Microduck approaches and kicks with its own legs, and the actor never gets to claim success β€” only the spotter's VERDICT, with the simulator's ground truth as a veto on top. Members are adapters sharing one arena and one lockstep clock, bids carry a capability term so a robot only bids for a role its manifest can fill, and a single auction fills every role. 10 of 10 seeds with the scripted pilots, zero planner LLM calls, every message in flock.jsonl. Simulator only, and the frame-of-reference limitation is written down rather than papered over (ADR-0020): docs/flock.md.
  • .duck v1 makes "wrong robot" a validation error instead of a runtime failure. requires, robots and flock.roles; v0 files parse unchanged and the bundled starters stay at duck: 0. quackd validate find-and-kick --robot reachy_mini:sim2d prints requires kick, but reachy-01 (reachy-mini) does not provide it and exits 1 before anything connects (ADR-0019): docs/duck-spec.md.
  • MCP fronts a fleet. serve-mcp --robots duck=microduck:sim2d,reachy=reachy_mini:mock exposes six robot_* tools (robot_list, robot_list_verbs, robot_run_verb, robot_observe, robot_say, robot_load_duckfile), each robot with its own executor, budget, heartbeat and contract, and robot_load_duckfile checks a contract's requires against that robot's manifest before adopting it. The eight duck_* tools remain as deprecated aliases of the default robot β€” 14 tools in all β€” and go away in 0.5. Every simulated robot over MCP gets its own world; a shared arena over MCP is future work: docs/mcp.md.
  • Also in 0.4: LAN discovery (quackd discover / quackd announce over zeroconf, an identity-only TXT record checked under 200 bytes per pair) and an MQTT flock bus behind the same two-method Bus protocol, both behind quackd[lan], both imported lazily, both library-only (ADR-0021, docs/lan.md); ADRs 0017 to 0023 and a page per adapter; goldens recorded from 0.3 that prove the seeded worlds, the starter ducks and the flock-kick conversation did not move; 360 tests, no network, no keys, and four seeded sweeps that CI holds at 10 of 10.

Nothing here has run on hardware, on any of the four bodies. What the demos and the sweeps use is microduck:sim2d, reachy_mini:sim2d and the mocks. The four SDK backends β€” microduck:jsonrpc, reachy_mini:sdk, lerobot:real, rosbridge:ws β€” are experimental in the strict sense: every upstream method, topic, socket and message name is read from pinned upstream source and marked VERIFIED or UNVERIFIED in one file per adapter, a test proves UNVERIFIED names are reachable only from those backends, and each has been exercised against fakes and nothing else. microduck:websocket is a stub that raises with links until upstream ships it. The Microduck arrives Christmas 2026; Reachy Mini hardware exists today and we do not have one. The LAN pieces are thinner still: zeroconf was exercised once for real between two processes on one machine, the MQTT bus once against a local amqtt broker on that same machine, and never between two machines β€” a flock across machines also needs a clock across machines, which does not exist yet, which is why there is no --bus flag.

uvx quackd run reachy-spots-duck-kicks --provider fake        # two bodies, one contract, no API key
uvx quackd run reachy-spotter --provider fake                 # a Reachy Mini head, no legs, same loop
uvx quackd list-adapters                                      # four adapters, eleven backends
uvx quackd validate find-and-kick --robot reachy_mini:sim2d   # "requires kick, but reachy-01 (reachy-mini) does not provide it"
claude mcp add quackd -- uvx quackd serve-mcp --robots duck=microduck:sim2d,reachy=reachy_mini:mock

quackd is an independent community project, not affiliated with or endorsed by Pollen Robotics, Hugging Face or Open Robotics.

Full changelog: v0.3.0...v0.4.0