PLAN.md
September 19, 2026 · View on GitHub
What is still open. Everything that has shipped is in CHANGELOG.md, the ADRs and the git history, which record it better than a task list can.
Legend: 🔨 in progress · ⬜ todo · ⏸ blocked (with reason)
Only a human can
Six bring-ups still need hardware quackd has never touched, one per body that has not met
any: the other six of the seven. The seventh happened, an SO-101 arm running
lerobot:real on 2026-09-15, which flipped its row in
docs/adapter-status.md. Each of the six ends the same way:
flip that backend's row, and not before.
- ⏸ An Open Duck Mini v2, the most reachable of the six because you can build it. Run
open_duck:bridgeagainst a duck you built, work through docs/open-duck-hardware-checklist.md, and confirm the deadman by pulling Wi-Fi mid-walk. Then the five numbers at the end of that checklist: boot time against the watchdog budget, camd's peak memory against its cap, the observed loop-rate floor, the camera's field of view against a tape measure, and the accelerometer upright versus on its side. The last one is what would give this robot fall detection, and quackd deliberately does not guess it, because a wrong fall detector fails as a confident "not fallen". - ⏸ A Microduck. Run
--robot microduck:jsonrpcagainst a realrobotdand work through docs/microduck-hardware-checklist.md, whose step 0 now rehearses the whole pilot in the physics simulator first. The path is built and audited: pinned at a commit and bumped to API v23 (it was v16 against a moving link, so the handshake would have refused), state actually subscribed to, and video overwebrtc://because upstream serves no frames on the socket. Pre-orders opened 2026-08-27, earliest arrivals estimated around Christmas 2026 and later orders four to six months out. - ⏸ A ToddlerBot on its safety stand, running
bridge/toddlerbot/quackd_toddlerbot_bridge.pythrough docs/toddlerbot-hardware-checklist.md. What most needs a real robot: whether the safe-pose slew is safe from a crawl, what tilt really means fallen, whether the neck axes are what the motor names imply, and whether a calibrated zero survives a restart. - ⏸ An XLeRobot. Start the host (it is commented out of upstream's own package
__init__and exits after an hour) and pointxlerobot-lookoutat it. What most needs a real cart: the camera colour order, whether+xis really forward, and whether the head motors are what upstream's agent library implies. - ⏸ An AlohaMini. Start
bridge/alohamini/quackd_alohamini_host.pyrather than upstream's host and pointalohamini-lookoutat it. What most needs a real robot: whether+xis physically forward, the camera colour order, how fast the lift travels in mm/s, and whether the wrapper really does leave the arms holding. - ✅ An SO-101 arm, on 2026-09-15: calibrated with upstream's own tool, then
lerobot-lookoutand twelve free-form goals, on a USB webcam atopencv://2, piloted bygpt-6-astra. What that afternoon left open, each a bring-up of its own: the rest pose, written after that day and driven on no arm;pickandload_policy(), because no policy was loaded; the registry path, because the arm was reached by--addressand never by a registered name; and that checklist's What to report, six things still chosen against Feetech's documentation rather than measured (ADR-0036). - ⏸ Any rosbridge base.
rosbridge:wsagainst a bridge. It is the one hardware backend with neither a lookout task nor a checklist. A coordinator flock across two machines needs a distributed clock first; a pilot flock needs none and has simply never been tried across two.
Open here
-
⬜ Nobody has run the discrete stepper against TypeSafe's own API.
--jevships off by default and exercised against a stub, so there is no agreement rate and no calibration curve, and the only latency figure anywhere is TypeSafe's own, on TypeSafe's own task.--jev shadowis built to produce all three without changing a run, onlerobot:mockor on a real arm. Until somebody runs it, the break-even in docs/jev.md is arithmetic rather than a result: on the wave run the stepper is a net loss unless Jev answers in under 1.24 seconds. The floors are TypeSafe's own published numbers and have never been tuned against quackd data. -
⬜ One local model has refused tasks on feasibility grounds; no frontier model has, and nobody has watched an
uncertainover MCP. Qwen3-32B-AWQ on vLLM, drivingmicroduck:sim2d, answered this gate 54 times before the check in #24 and 54 times after. Before: 14infeasible, 26uncertain, 14feasible, with a noise floor around 2 per six run cell (@Vallhalen, #24, written up in docs/local-llms.md). It refuses a categoricalcannotreliably, it hedged on one, and the figure nobody published was the hole the check now covers: on the eight tasks the check never fired on, the verdicts moved by 1 to 2 either way, which is that floor rather than a result. That is one model, one quantisation, one simulated body and three repeats a cell. Still open: whether a frontier model usesuncertainwhen it should or reaches forinfeasibletoo readily, which thelive_llmtests measure and which needs a key; a pilot that never names the figure its plan hinges on, which the check reads nothing about and cannot catch; anduncertainover MCP, where the verdict stays pending and the model is told to ask the person it is chatting with, which no real session has been watched doing. -
⬜ Whether the words of
assess_taskfixed #25.--goal "Find the ball and kick it."stopped at this gate 5 times in 6 on that same model where the shippedfind-and-kickfile passed 6 of 6, and the allowlist width is the measured cause. The description it read has been corrected rather than the allowlist narrowed, and nobody here runs that model, so the six cells want running again on a build that carries the change (#25). -
⏸ A flock role can ask for a body, and no coordinator flock can have two different ones.
flock.roles.<role>.needsvalidates, and the coordinator matches it against the datasheet a bid carries, tested at that level. Butflock/runner.pystill knows only the Microduck, so nothing quackd can start exercises the matching end to end. It waits on the same work as the rest of heterogeneous coordinator flocks, including the latent bug ADR-0020 records: the coordinator judges eligibility before members report their vocabulary, andneedsinherits that. A pilot flock does put two different bodies on one task, and it uses no roles: each pilot reads its own datasheet and its peers', and they divide the work by talking (ADR-0034). -
⏸ No pilot flock has been driven by a real model, or by a real robot.
flock-helloruns onmockandsim2dbodies with the scripted rule, which cannot reason about a datasheet, so what a frontier model does with theYour flocksection andtellis unknown. Needs a key, and then two robots. -
⏸ N simulated pilots are N separate worlds. Two
microduck:sim2dmembers of a pilot flock cannot see each other, so nothing checks a claimed success against ground truth the way the coordinator's arena does. A shared arena for pilots is unbuilt. -
⏸ Nobody has asked a real bridge what its robot is.
rosbridge:wsreads the topic list and the URDF at connect, from the parameter and from the latched topic, and every name is VERIFIED at a pin. All of it is exercised with fake services and fake topics. What a real bridge does, whetherrosapiis running, whether the description is where its defaults expect, and what a real robot's inertials add up to, is unknown until somebody points it at one. -
🔨 Somebody has to drive
web/in a browser, and record it. The page itself is no longer unopened: it booted clean twice on the machine that wrote it (8d72a2a,a9fea18), with the fonts and the mark loaded and a heldWwalking the duck. What that leaves is everything past the boot — a full model-driven run, a key barging in out of one, the Record button, the switch thrown mid-run, and any browser or machine but that one. None of it was recorded, so there is no asset and nothing anybody else can check. Separately, the four measured claims inweb/README.mdcome from a scratch harness that is not in the repository, and both files it measured have changed since, in the abort path and in the arena's geometry, so nothing here can re-run it. Locally it ispython web/serve.py, then http://localhost:8000/simulator/. -
⬜ The coordinator flock does not know
open_duckyet (flock/runner.pyknows one adapter). A pilot flock knows all seven. A hardware flock of either kind waits on robots shipping. -
⏸ A real model recording, in either simulator, to replace a scripted-pilot asset and drop the label (see docs/assets). Needs a key.
-
⬜ The browser demo is not at parity with the backend. Seven of the manifest's fifteen verbs and none of the three composites, a contract of its own, an arena that is not upstream's scene, geometric perception, no hash check on anything it fetches, a seed that means the same distributions and not the same layout, and no scripted pilot. It also has no datasheet and no feasibility gate:
web/src/pilot.jsis a second loop with its own hardcoded prompt, so a page asked to carry something will try.web/README.mdholds that list in full and is the one place it is kept; this bullet is the reminder that it is a list of open gaps and not just a disclosure. -
⬜
GAIT_FLOOR_VYwas never measured. The forward and turning floors were; the sideways one is assumed equal to the training maximum, so every lateral request is sent at full scale. The assumption is inGAIT_THRESHOLD's note and in the state'sassumptions, and the fix is the same script that produced the other two. -
🔨 A transcript from a live local server quackd has not seen yet (Ollama, llama.cpp). Still none on the dev machine, and that has not changed. LM Studio was covered by #7 and vLLM by #23, which also closed the other half of this item: its two Qwen3-32B-AWQ runs are a chain, the note the first one saved is in the second one's
system_promptverbatim, and the memory counters move by exactly one note and one episode, so nothing ran between them. Both ends are indocs/assets/transcripts/, read in docs/local-llms.md. What is still open is Ollama, llama.cpp, a run on this machine, and any task harder than the starter duck. -
✅ Both nightly jobs are green, and this release is what made them so. Neither had ever passed a scheduled run:
microduck assetsred since 2026-09-09,toddlerbot contractsince 2026-09-07. Four failures, and every one of them was the job telling the truth. The gait sweep went 9 of 10 because the physics backend raises a small twist to a gait floor measured at 0.22 on MuJoCo 3.12, and the lock resolves 3.13, where 0.22 walks the duck thirteen millimetres in ten seconds. A ball dead ahead was unreachable. The floor is 0.23, re-measured across all ten seeds, and the achieved fraction with it is 0.38 where it was 0.42. The fall-recovery test asserted a heading of π on a pose that is exactly the gimbal singularity, so both arguments to the yaw'satan2are zero and the sign is the last bit of a subtraction: π here, 0.0 on the runner. It asserts the degeneracy itself now.standnever finished on the ToddlerBot because the slew advanced from the measured pose each tick, which pins the position error at one step and starves a position-controlled motor of torque; it advances from the last commanded target now and completes in the 5.2 seconds its own arithmetic predicts. And the deadman test asked for the daemon's health over the very socket it had just killed on purpose. The record is short and worth stating as it is: both jobs went green on a manual run on 2026-09-17, the day the fixes landed, and the first scheduled run after that, on 2026-09-18, was green for both. That is one scheduled green run each, not a record. -
✅ Exercise
rememberagainst a cloud model.gpt-6-astracalled it in seven of its twelve runs on the arm on 2026-09-15, for five distinct notes, and the last run of that afternoon read all five back out of its own prompt. Still open in a simulator, where the scripted pilot has no script for it, so--provider fakewrites episodes and never a note. -
⏸ Upload
docs/assets/social-preview.pngunder Settings → Social preview. There is no API for it, so it is the one asset a commit here cannot ship, and it is now a version behind: the card was rebuilt around the duck head the README and quackd.org both use, so the one GitHub serves is still showing the flat biped and the two-colour wordmark that no longer exist anywhere else. The card is otherwise current: it carries the one-liner (ADR-0035) and its two panels are a real three-robotsim2darena. Regenerate it withdocs/assets/social_preview.py, which exists because the original was drawn by hand and the script was never committed, so nobody could. -
⏸ No asset shows a flock of pilots.
flock.gifis the coordinator: three identical ducks auctioning a kick. The kind of flock the README now leads with is two different bodies talking, and it has no recording, because a pilot flock writes no GIF (N members are N worlds).
Open elsewhere
Things no commit in this repository can finish.
- ✅ The GitHub About text and Topics carry the one-liner (ADR-0035), set by hand because there is no commit that can set them. Topics are at the cap of 20, so the next one has to replace one.
- ⏸ The landing page. https://www.quackd.org/ is built from quackd-web, a separate
repository, and it was written around 0.5: quackd was a brain for one small robot, there were
five adapters, and one of the five was the Reachy Mini, which this project removed in 0.9. Its
copy is being corrected there. The part no commit in either repository can fix is
public/og.png, the card a social network shows for quackd.org. It is a designed asset with no generator, and its headline is set in a display weight of Nunito Sans that Google now serves only as a variable font, so it needs whoever made it rather than a script. - ✅
web/is on the web. https://www.quackd.org/simulator answers. That address belongs to quackd-web, a separate Vercel project serving the landing page, and its build now fetches this directory into its own/simulatorat a pinned commit — so a change here reaches the page on that project's next deploy, and/simulator/source.jsonrecords which commit the live copy came from. The landing page points at the demo from five places; the demo's header points back.
Release checklist
The one reusable thing the shipped milestones left behind. Every release since 0.1.0 has run this, and the per-release detail is in CHANGELOG.md.
- All five CI gates green on
main:uv lock --check,ruff check,ruff format --check,mypyon 3.11 and 3.12,pytestwithQUACKD_STRICT_SEEDS=1, plusquackd validate ducks/*.duckand thepackagingjob, which proves a bare core install brings no robot and says what to install. uv run python scripts/set_version.py X.Y.Zand thenuv lock. Eight packages carry a version and they are released together, so none of them may drift.- Read the release note against the code before it ships. Every release so far has found claims that had gone stale between writing and tagging.
- Annotated tag, pushed with
main. uv build --all-packages --out-dir dist: eight wheels and eight sdists. GitHub Release onmainwith all sixteen attached.- Publish to PyPI core first, because every adapter depends on it and a resolver that
meets
quackd-lerobotbeforequackdhas nothing to resolve against. Then check the SHA256 of all sixteen files is identical in both places. uvx --from "quackd[microduck]==<version>" quackd run find-and-kick --robot microduck:sim2d --provider fakefrom a clean install, twice, so the second run reads the first one's episode. Thenuvx quackd run find-and-kickwith no extra, which must refuse and name what to install rather than running anything.- Update the About description (GitHub's cap is 350 characters) and Topics (cap 20).