Roadmap: the first one reached its target, so this is the next one - #158
Merged
Conversation
M1 to M4 were a sequence with one destination — a robot that walks, updates itself over the air, and cannot be bricked doing it. That arrived at 0.9.x. The work left is independent tracks with no ordering between them, so the milestone numbers stop being a sequence and an "order of work" section carries that instead. Three milestones are new: - **M7 — knowing what a board is doing.** A duck in someone else's hands will develop a fault, and today the answer to *which part* is a developer reading a journal over ssh. Framed as the questions it owns rather than a design: a support bundle, a hardware pass/fail a non-developer can run, history for intermittent faults, and the overlap with #52 — whose crash record is exactly the durability a fault report needs. M4's two unmeasured numbers land here. - **M8 — the model channel.** Policies ship inside the daemon artifact today, so a new gait needs a daemon release. The engine was designed for the other arrangement and most of it exists: the `hf_hub` source verifying our own minisign, per-model components with their own version line and rollback, and `action = "reload"`. What is missing is at the ends — `robotd` cannot reload, nothing publishes a bundle, and publishing a policy is a third kind of signing key. - **M9 — the autonomous brain**, with the shape argument that matters: presence, mood and the beat are inputs to one brain, not modes beside it. M4 closes with its log criterion rewritten. It asked for logs surviving a power cut; `/var/log` is zram and the durable record is the update history, so the criterion was asking for a property this project decided not to have. What is left of it is one `journalctl --list-boots` on a board. M5 gains the Python client question — whether one client covers both transports, WebSocket for control and snapshots and WebRTC only for a stream — and splits privacy in two, because consent is unblocked and the indicator waits on hardware that does not exist. M6 is reordered by lead time rather than difficulty, and promotes what was an M2 footnote: a private repo cannot serve the fleet, so as things stand robots in the field cannot download anything. With preorders open that is the longest-lead decision on the page. Assisted-by: Claude:claude-opus-5[1m]
**The repository goes public**, which closes §6.1. It was the longest-lead item in M6 on the grounds that a robot shipped pointing at the wrong host cannot be updated; going public means there is no second host and no token in an image. The section keeps the options and the reasoning rather than deleting them — an artifact-only public repo is the fallback if the source ever has to close again — and records what going public does *not* change: the engine's API path is already the one path for private and public alike. What it does leave is a budget, written down where someone will hit it. Anonymous GitHub API requests are capped at 60 an hour per IP and a home robot has no token, so its checks spend from the pool shared by everything behind that address. One duck at `check_interval = "6h"` is nowhere near it; a room of twenty on one wifi is not. Not a correctness problem — `http.rs` already reads 403 and 429 as "come back later" — but a reason to fetch bytes through `browser_download_url` once public, since object downloads spend nothing from that pool. **Models: browsing is a trust decision, not a plumbing one.** The ask is basic policies of our own plus an easy way to look at and download others, and the second half is what needs deciding: every artifact the engine installs is verified against a trusted key, and a stranger's model is signed by nobody this robot trusts. Recorded as three things to settle inside M8 — curated org or an explicitly unverified path that is off by default and refused on a customer robot the way dev keys are; the `obs[1,61] -> actions[1,14]` shape check becoming a hard gate whoever signed it; and the fact that makes it allowable at all, which is that the safety layer holds the only write handle to the bus. Slots stay fixed and sources do not: repointing `walk` at another repo keeps rollback, pin and known-bad per slot, where letting arbitrary components appear at runtime would mean the config is no longer the authority on what a robot may run. Assisted-by: Claude:claude-opus-5[1m]
…n broken The lean is that they leave and the updater fetches them from the Hub. Written down with the edge that decision has, because it is not the obvious one: a policy that was wanted and could not be loaded reports *unhealthy*, so a board with no models installed would fail the health gate on every subsequent daemon update and roll back — an update loop caused by a file the update could not have supplied. `HealthResult::degraded` already answers it, and was built for this exact shape: a condition that belongs to the board rather than to the release being gated, where reverting cannot fix it and only churns the boot counter. Which model components are installed is that, so the gate commits and health names the missing policy. The other consequence — no gait on a flashed board with no network — goes where the same dependency already lives: provisioning installs the bootstrap set, as it already installs the ONNX runtime and the GStreamer plugins. That also leaves exactly one source for a policy at runtime, so there is no precedence rule to write between a release copy and a Hub copy. The floor-plus-override alternative stays recorded with what it buys, since the decision is a lean rather than a settlement. Assisted-by: Claude:claude-opus-5[1m]
Everything the rewrite added about going public was written in the future
tense — "the repository goes public", "until it does", "until this week" — and
all of it reads wrong the day the flip happens. The decision is what these docs
record, so they now record the decision: *publish this repository*, phrased so
it is true on both sides of the switch and needs no second pass afterwards.
§6.1's heading loses its "so this one goes public" for the same reason.
`install-dev.md` had the sharper version of the problem: it walks a reader
through baking a GitHub token onto a board and `chmod 600`-ing it, listed as a
flat prerequisite. Once the repo is public that credential buys nothing but a
higher rate limit, and a reader following the steps unchanged would put one on a
board for no reason. The prerequisite and the by-hand section now say when a
token is actually needed — while the repo is private, or for the anonymous API
budget §6.1 documents. `provision-board.sh` already said this ("only while the
repository is private"); the docs were behind the script.
Two smaller things a public reader would hit:
- The link to `microduck_rl` 404s for anyone outside the org, and it is M8's
central artifact. De-linked and named as private, rather than pointing a
stranger at a page they cannot open.
- M6 no longer carries a delivery estimate. "Preorders are open" is the whole
reason the section orders by lead time; "shipping is a few months out" is a
schedule commitment in a versioned file, doing no work in the argument.
Not touched here, because it is a decision rather than an edit: `ci-setup.md`
states in bold that anyone with push access can read the release signing key,
and why no protection rule can be turned on. The roadmap links to it. That goes
public in the same flip and is worth settling first.
Assisted-by: Claude:claude-opus-5[1m]
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.
M1 to M4 were a sequence with one destination — a robot that walks, updates itself over the air, and cannot be bricked doing it. That arrived. The work left is independent tracks with different risks and no ordering between them, so the milestone numbers stop being a sequence and the order of work becomes its own section.
Three new milestones
M7 — knowing what a board is doing. A duck in someone else's hands will develop a fault, and today the answer to which part is a developer reading a journal over ssh. No design, on purpose — it is framed as the four questions it owns: a support bundle (nothing collects health, versions, journal, update history and unit states together), a hardware pass/fail a non-developer can run, history for the intermittent faults a snapshot cannot show, and the overlap with #52 — whose crash record, flushed to
/var/liboutside release dirs, is exactly the durability a hardware fault report needs. The two should be decided together rather than growing two answers. M4's two unmeasured numbers land here.M8 — the model channel. Every policy ships inside the daemon artifact today, so a new gait needs a daemon release and a policy trained on a laptop reaches a duck only through CI. The engine was designed for the other arrangement and most of it is already built: the
hf_hubsource verifying our own minisign (HF signs nothing for us), a model as an ordinary component with its own version line, install dir, rollback target, pin and known-bad history,action = "reload"implemented, andxtask signsigning any directory.What is missing is at the two ends:
robotdhas no SIGHUP handler and no way to swap anortsession under a running 50 Hz loop; nothing publishes a bundle (xtask package --channel model-walkchecks--versionagainst a crate version a model does not have);model_apiis designed and unimplemented; and publishing a policy is a third signing key, not a reuse ofrelease-1orteam.dev. One decision comes first — whether the release keeps its copies as a floor that a Hub component overrides, or the network becomes a hard dependency for a robot that walks.M9 — the autonomous brain. The biggest untracked gap, with the shape argument that matters: presence, mood and the shared beat are inputs to one brain, not modes beside it. The chorale and the theremin grew as modes because there was nothing to hang them on, and the chorale is now 55 KB of
robotd.What changed in the existing ones
M4 closes, with its log criterion rewritten. It required logs to survive a power cut;
/var/logis a zram device and the durable record is the update history under/var/lib, so the criterion asked for a property this project decided not to have (deploy/README.mdrecords the decision). What is left is onejournalctl --list-bootson a board.M5 gains the Python client question — whether one client covers both transports, or the WebSocket surface alone is what a script wants — and splits privacy in two: consent is a
mediadsession-layer change and is unblocked, the indicator needs an LED under software control that does not exist and should be asked of the hardware rather than parked here.M6 is reordered by lead time rather than difficulty, and promotes what was an M2 footnote: a private repo's release assets are reachable with a token and a customer robot has none, so as things stand robots in the field cannot download anything (
updater-design.md§6.1). With preorders open, that is the longest-lead decision on the page — it changes whererelease.ymlpublishes and whatupdater.tomlon every shipped robot points at.Also
remote-webrtc.mdM6,architecture.mdM2,robotd-design.mdM3) still point at the right sections.