Add container dev mode guide and field note - #473
Conversation
Capture the disposable-QEMU engine-VM lab that exercised Container Dev Mode's authenticated push path end-to-end (single-layer sync, plus the two bugs the lab caught: plain-HTTP write path and the 2 MiB body limit). Kept draft: true with [ENGINEER:] gaps because the CLI and device extension are pre-release; a reader cannot reproduce it until the feature ships. Flip draft to false once it lands and the steps are re-verified on a released build. Signed-off-by: Javier Tia <javier@peridio.com>
The Container Dev Mode VM-push draft carried 14 em dashes; the sibling field notes use at most one, so the section's house style is plain hyphens. Convert them now so the prose matches before the draft reaches the correctness review gate, leaving the release-blocked [ENGINEER:] placeholders untouched. Signed-off-by: Javier Tia <javier@peridio.com>
Docusaurus rejects `image: ''` ("does not look like a valid url"), so the
dev server refused to compile the note. The image field is optional -
omitting it renders the index placeholder box - so remove the empty
value rather than invent a path before the note has real art.
Signed-off-by: Javier Tia <javier@peridio.com>
Container Dev Mode ships a new CLI surface (a container_dev config block and five avocado container dev subcommands) plus a device-side agent extension, and nothing in the docs told a user how to use any of it. The only written material was a field note about how we validated the push path internally, which is not a how-to. Add a guide under Development covering prerequisites, the config block, the loop, and reference tables for the commands, environment variables, and default ports. It sits next to hardware-in-the-loop because it is the same iterate-against-a-live-device bargain applied to containers, and it states the dev-only scope up front so nobody mistakes it for a production container delivery path. Signed-off-by: Javier Tia <javier@peridio.com>
The draft told the story from the implementer's chair: how a throwaway QEMU lab validated the authenticated push path. A reader learned how we tested the registry internals, not what the feature does for them or why they would want it, so the note failed the audience it exists for. Lead with the loop the reader already lives in - a multi-gigabyte inference container where one iteration costs the size of the image rather than the size of the change - and show the config block, the three commands, and what actually moves. The QEMU lab and the two bugs it caught become the credibility half rather than the premise, and the how-to now lives in the guide, which the note links to instead of restating. Drops the remote-build bullet: an NFS/SQLite detail about our own build infrastructure that told the reader nothing about the feature. Signed-off-by: Javier Tia <javier@peridio.com>
Both pages described the hot-reload loop in prose and a numbered list, which made the reader assemble the shape themselves: where the layer crosses, what runs on the host versus the device, and that the cycle closes back to the next edit. The device's role as a live participant was the part most easily missed. Add the same diagram to the guide and the note, drawn as a text block with the repo's existing box-drawing and magic-comment colors rather than a new Mermaid dependency, since six pages already use that convention and the prism magic comments work in blog posts too. Colors follow core-concepts: orange for what you do, green for the CLI, blue for on-device. It also makes explicit that a QEMU target and a physical board are the same thing to this loop, both being SSH-reachable devices. Signed-off-by: Javier Tia <javier@peridio.com>
The docs CI runs prettier --check, and neither file was formatted: the
reference tables in the guide were unaligned, and prettier reflowed the
note's PullQuote.
Running prettier also exposed a real defect. Prettier's markdown parser
reads the asterisks in an MDX `{/* ... */}` comment as emphasis and
rewrites them to underscores, producing `{/_ ... _/}`, which MDX then
tries to evaluate as a JS expression and fails on with "Could not parse
expression with acorn". The pre-release note therefore moves into the
frontmatter as YAML comments, which prettier preserves and which never
render, next to the existing internal-drafting comment block.
Signed-off-by: Javier Tia <javier@peridio.com>
There was a problem hiding this comment.
🟡 Not ready to approve
The new guide has a confirmed docs-link convention mismatch and an internally inconsistent config-key reference table that should be corrected for clarity and consistency.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
Adds end-user documentation for the new “Container Dev Mode” feature by introducing a dedicated Development guide and a supporting (draft) field note, and wires the guide into the Guides sidebar.
Changes:
- Add a new “Container dev mode” guide describing setup, workflow loop, command/env/config references, ports, and trust model.
- Add a new draft field note reframing the feature around the developer inner loop and linking readers to the guide.
- Update the Guides sidebar to include the new guide under Development.
File summaries
| File | Description |
|---|---|
| src/sidebars-guides.js | Adds the new guide doc ID to the Development section navigation. |
| src/field-notes/2026-07-23-container-dev-mode-vm-push.mdx | New draft field note describing the workflow and validation story, linking to the guide. |
| src/docs-guides/container-dev-mode.md | New end-user guide documenting Container Dev Mode usage and reference material. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 2
- Review effort level: Low
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
jetm
left a comment
There was a problem hiding this comment.
Cold second read on my own PR. Six findings inline, plus both of Copilot's are valid and I am queuing fixes for them.
The one worth stopping on: the guide has no draft/unlisted and is wired into the sidebar, so merging publishes it to the live docs - while the field note in this same PR is held at draft: true with a comment saying the feature has not shipped and a reader cannot reproduce it today. That reasoning applies to the guide unchanged. container_dev is on zero files on avocado-cli origin/main and 15 on the unmerged branch, and a production build emits the guide's HTML while emitting nothing for the note.
Verified by building: npm run build exits 0. Worth noting explicitly that this is weak evidence for the note and not for the guide - Docusaurus drops draft: true posts in production, so the passing build validates the guide and says nothing at all about the note.
Also checked and clean: all 5 avocado container dev subcommands, all 6 AVOCADO_CONTAINER_DEV_* env vars, all 4 config keys, the three default ports, the sidebar id, every internal link under onBrokenLinks: 'throw', the highlight directives, the truncate marker position, authors: [jtia], and the date/filename match.
…nk extension The configuration reference gave the first key as `runtimes.<name>.container_dev` and then dropped that prefix for the three below it. Read on its own, `container_dev.images[].ref` looks like a top-level key - which the same page warns three sections earlier is exactly what does not work: "the presence of a `container_dev` block under a runtime turns it on for that runtime. A block placed anywhere else is not honored." So the table quietly contradicted the prose it was meant to summarize. Qualify all four rows. The paths match the CLI's own types - `ContainerDevConfig` carries `images` and `registry`, `ContainerDevImage` carries `ref` and `service` - so a reader can paste the dotted path against the YAML example above and land in the right place. The intro also linked `./hardware-in-the-loop.md` where every other link in docs-guides is extensionless; seven of them are, and this was the only one that was not. Beyond consistency, the extension is the part that breaks if a target is ever renamed to `.mdx`. Prettier reformatted the table's column widths after the longer keys went in. Worth noting the file passed `prettier --check` before this change and the hand-aligned padding broke it, so the reformat is part of the fix rather than incidental churn - checked both before and after with the repo's own `src/.prettierrc.json`. Signed-off-by: Javier Tia <javier@peridio.com>
There was a problem hiding this comment.
🟡 Human review recommended
The documentation is explicitly pre-release and dependent on multiple external PRs/releases, so correctness and publish-readiness can’t be fully validated from this change set alone.
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 0 new
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
…vent The guide's headline promise is that a normal `docker build` triggers the reload, and the watcher genuinely does read tag events from the engine event stream. But BuildKit emits no tag event when it tags a build result, and BuildKit is the default builder on current Docker, so following the guide as written leaves the reader watching a device that never updates with nothing in any log to explain it. Document both escape hatches at the point the reader builds, and recommend `container dev sync` over `DOCKER_BUILDKIT=0`: Docker already prints a deprecation notice for the classic builder, so the flag buys time rather than a fix, while the explicit trigger does not consult the event stream at all. Verified both paths against the lab VM before writing them down. The architecture section previously asserted the event fires; it now points here instead, so the promise and the caveat cannot drift apart. Signed-off-by: Javier Tia <javier@peridio.com>
There was a problem hiding this comment.
🟢 Ready to approve
The documentation changes are internally consistent, follow existing docs conventions, and introduce no verified build/MDX issues in the modified files.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 0 new
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
… it needs Every code block on the page was labelled "On Host", and the page never once showed a command run on the device. A reader following it therefore had no way to learn where their container actually runs, and the first thing they try when the loop looks stuck - `docker logs my-app` on their workstation - returns "No such container", which reads as a broken feature rather than as the wrong machine. Name the two sides up front and borrow the vocabulary the site already has: the hardware-in-the-loop guide does for extensions what this page does for containers, uses "target" for the far side, and is already linked from the intro. Calling it the HIL target rather than "the device" ties the two pages together and says which machine is meant without a reader having to infer it. The new section carries the `docker logs` case explicitly, with the target-side commands that do work. The page also never stated that the `service` it restarts has to exist already. Nothing said who creates it, so the reasonable reading is that container dev mode does, and a reader hits an unexplained failure the first time they configure one. Say it in Prerequisites, and show the ExecStart shape there, because a unit that restarts a container instead of re-running `docker run` fails silently - the layer pulls, the restart succeeds, and the old image keeps running. Signed-off-by: Javier Tia <javier@peridio.com>
There was a problem hiding this comment.
🟡 Not ready to approve
A docs-guides link/terminology consistency issue should be addressed before merging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Suppressed comments (1)
src/docs-guides/container-dev-mode.md:99
- This sentence mixes an extensionful relative doc link ("./hardware-in-the-loop.md") with the extensionless style used elsewhere in docs-guides, and also introduces the acronym "HITL" after consistently using "HIL" in this guide. Use an extensionless link and avoid the acronym here to keep terminology and link style consistent.
This is the same host/target split as [hardware in the loop](./hardware-in-the-loop.md), which does for extensions what this page does for containers. If you already run HITL, the mental model carries over unchanged.
- Files reviewed: 3/3 changed files
- Comments generated: 0 new
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
…mons The caution said BuildKit emits no tag event, so the watcher cannot see a plain `docker build`. That was measured against one daemon, docker 20.10.24, and written up as a property of BuildKit. It is a property of the daemon version. Docker 29.6.2 emits `image tag` for a BuildKit build and the loop runs unattended end to end; 20.10.24 emits nothing. As written the page told most readers to work around a problem they do not have, and pushed them toward a builder Docker has deprecated. State the version boundary instead, give the one-line check for the daemon that runs their builds, and keep both workarounds for anyone actually on an old one. The exact release that changed is not claimed, because only those two endpoints were measured - pre-23 is called out as the conservative line. Signed-off-by: Javier Tia <javier@peridio.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 11 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- src/package-lock.json: Generated file
Suppressed comments (2)
src/docs-guides/container-dev-mode.md:135
- The agent is referred to as
container-agent-devthroughout this guide, but this command usesavocado-container-agent-dev, which doesn’t appear anywhere else in the repo and likely won’t match the actual systemd unit name (sojournalctlwill show nothing / error). Use the same unit name you document elsewhere.
docker logs -f app # the target's engine, where your container runs
journalctl -u app.service -f # the service the agent restarts
journalctl -u avocado-container-agent-dev -f # pull + restart, as the agent sees it
src/field-notes/2026-08-07-container-dev-mode-imx93.mdx:137
- In the
avocado.yamlsnippet, the SSH extension is listed assshd-dev, but elsewhere in this repo (schema examples and other docs) the canonical extension ID isavocado-ext-sshd-dev. Using a different name here will cause readers to copy a config that can’t resolve/install the intended extension.
- docker # the container engine
- container-agent-dev # the device agent
- sshd-dev # up delivers the session bootstrap over SSH
The note carried the date of the first hardware run. It has been reworked substantially since then and the loop was re-run end to end on the board today, so that date now describes an earlier draft rather than what is being published. The filename moves with it because the permalink is derived from it, and a note whose URL says one date while its byline says another is the kind of mismatch that outlives the reason for it. Nothing links here yet, so the permalink change costs nothing now and would cost a redirect later. The internal pre-release note's tested-on date moves too. It is the same claim about the same run, and leaving it behind would have it contradict the line above it. Signed-off-by: Javier Tia <javier@peridio.com>
…ot pgrep The re-run section told the reader to hunt the `up` process with `pgrep` and said nothing about ending it. That was my workaround leaking into the note: `dev` has had `status` and `down` all along. Hunting a PID is worse than wrong here, because killing the process directly skips the teardown - `down` signals the session so every listener closes, the authenticated write port included, and it clears the session state even when the process behind it is already dead, which is the case the reader is actually in when `up` refuses to start. The store paragraph now also says why `prune` is not the command for that job. It sweeps blobs no currently-tagged manifest references, which is exactly the set a reader re-measuring a first delivery needs gone, so reaching for it looks right and changes nothing. The internal dependency section carried its own removal instruction in an MDX comment, which is invisible in the render - so the one artifact that must not ship announced itself only to whoever opened the source. It is an admonition now, naming what to delete and what to pin, on the same reasoning as the video placeholder: a reviewer reads the page. Signed-off-by: Javier Tia <javier@peridio.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 11 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- src/package-lock.json: Generated file
Suppressed comments (2)
src/docs-guides/container-dev-mode.md:97
- The guide explains there are two thick amber network hops (control notify + bulk pull), but the diagram only thickens one link (
linkStyle 7). This makes the “two such hops” explanation inaccurate and reduces the visual cue for the bulk-transfer path. Consider thickening both network links (notify + bulk read) or adjust the prose to match the single thickened hop.
%% Link 7 is the one arrow that leaves your host, thickened so the single
%% network hop is the most visible edge in the figure. Indices count every
%% link in declaration order, and a chain contributes one per arrow.
linkStyle 7 stroke:#d97706,stroke-width:3px
src/docs-guides/container-dev-mode.md:135
- This command uses
journalctl -u avocado-container-agent-dev, but the rest of the docs in this PR refer to the agent ascontainer-agent-dev(and the field note showscontainer-agent-dev.service). Using a single unit name avoids sending readers to an empty journal when they troubleshoot the loop.
journalctl -u avocado-container-agent-dev -f # pull + restart, as the agent sees it
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 11 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- src/package-lock.json: Generated file
Suppressed comments (2)
src/docs-guides/container-dev-mode.md:97
- The prose says there are two cross-network hops, but the diagram only thickens one link (and the comment says “single network hop”). This makes the visual cue inconsistent with the explanation and can mislead readers about the two-port requirement.
%% Link 7 is the one arrow that leaves your host, thickened so the single
%% network hop is the most visible edge in the figure. Indices count every
%% link in declaration order, and a chain contributes one per arrow.
linkStyle 7 stroke:#d97706,stroke-width:3px
src/docs-guides/container-dev-mode.md:135
- This guide refers to the agent unit as
avocado-container-agent-dev, but the rest of this PR (e.g., the i.MX93 field note) usescontainer-agent-dev.service. Using two different unit names will cause the suggestedjournalctlcommand to fail for some readers.
journalctl -u avocado-container-agent-dev -f # pull + restart, as the agent sees it
…e cleanup The walkthrough uses `up` and `sync` and never says the other three exist, which is how a reader ends up inventing a teardown. Naming all five in one table costs a screen and closes that gap. The cleanup is the part with consequences. The registry store lives on the host and only grows, and what accumulates is not the tagged layers but the staging files from pushes that died mid-flight: a session killed by a power cut or an OOM reaper leaves its partial layer behind and nothing else ever removes it. A reader who never learns `prune` exists finds this as unexplained disk usage months later. `down` before `prune`, and the note says why rather than asserting an order: prune refuses while a session is live, because that session may be streaming a blob or holding a staging file the sweep would unlink underneath it. The scoping trap gets a line too - the top-level `avocado prune` removes Docker volumes and is a different command with a confusingly similar name. The closing pointer now offers the guide for the flag reference rather than for "the full command set", which this section has become. Signed-off-by: Javier Tia <javier@peridio.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 11 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- src/package-lock.json: Generated file
Suppressed comments (2)
src/docs-guides/container-dev-mode.md:97
- In this Mermaid diagram,
linkStyle 7does not target a network hop. With the current declaration order, link 7 isbuild --> watcher(host-only). The two edges that actually cross host↔target arecontrol --> agentandregistry --> agent, so the styling/comment currently mislabels the thick network arrows described in the surrounding prose.
%% Link 7 is the one arrow that leaves your host, thickened so the single
%% network hop is the most visible edge in the figure. Indices count every
%% link in declaration order, and a chain contributes one per arrow.
linkStyle 7 stroke:#d97706,stroke-width:3px
src/docs-guides/container-dev-mode.md:135
- The guide consistently calls the device-side unit/agent
container-agent-dev, but this command usesavocado-container-agent-dev, which doesn't appear anywhere else in the docs and likely won't match the actual systemd unit name. Using the same unit name as elsewhere (e.g.,container-agent-dev.service) makes the command copy/pasteable.
journalctl -u avocado-container-agent-dev -f # pull + restart, as the agent sees it
The note tells you not to build a BSP, and its own steps take the prebuilt image, so nothing here was wrong. But it names meta-avocado #259 as where the board's image comes from, and #259 now sits on #268, which makes image content opt-in per feature group. Anyone who follows that pointer and builds gets a feed with no container engine and no SSH server, and the note would have sent them there without warning. One clause, in the caveat that already exists, rather than a reproduction path this note does not use. Signed-off-by: Javier Tia <floss@jetm.me>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 11 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- src/package-lock.json: Generated file
Suppressed comments (2)
src/docs-guides/container-dev-mode.md:135
- This
journalctlexample usesavocado-container-agent-dev, but the rest of the guide (and the field notes) refer tocontainer-agent-dev. Using the wrong unit name will point readers at an empty journal.
journalctl -u avocado-container-agent-dev -f # pull + restart, as the agent sees it
src/docusaurus.config.js:142
- The comment says the Mermaid theme is “Pinned” to @docusaurus/core, but
src/package.jsonuses a caret range (^3.10.1), which isn’t actually pinned. Either pin the dependency version or adjust this comment to avoid implying a stricter guarantee than the config provides.
// Pinned to the same version as @docusaurus/core; a theme package on a
// different version than core is the usual source of Docusaurus theme skew.
The Checks job failed on `prettier --check`, on this file alone: the pipe table's
cells were padded to the width of its second-longest row rather than its longest,
so every other row was six columns short.
Worth recording why this was not simply `prettier --write` and done. The file
carries a comment warning that a markdown formatter reading an MDX comment's
delimiters across two lines rewrites them to `{/_ _/}`, which is not an MDX
comment and fails compilation - and a Copilot review on an earlier revision
reported exactly that breakage. So running the formatter over this file is the
one operation that could reintroduce it.
It does not, because all three comments sit on one line as that warning
instructs: the write touches six table rows and nothing else. Confirmed by
building the site afterwards, which is what actually compiles the MDX.
Signed-off-by: Javier Tia <floss@jetm.me>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 11 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- src/package-lock.json: Generated file
Suppressed comments (1)
src/docs-guides/container-dev-mode.md:97
- The diagram legend says there are two network hops (control notify and bulk pull) highlighted with a thick amber arrow, but only one link is currently styled (
linkStyle 7). Also, the inline comment says “Link 7 is the one arrow that leaves your host”, but there are two host↔target edges in this diagram. Style both network links (control→agent and registry→agent) so the diagram matches the legend.
%% Link 7 is the one arrow that leaves your host, thickened so the single
%% network hop is the most visible edge in the figure. Indices count every
%% link in declaration order, and a chain contributes one per arrow.
linkStyle 7 stroke:#d97706,stroke-width:3px
The note stops at a working hot-reload loop, which leaves the first question anyone asks about a device unanswered: does it still work after the power goes out. The answer splits in a way that is worth writing down, because one half fails silently. The image survives on its own - docker's root is on the /var partition, so a synced image is still there and nothing re-pulls. The unit does not, when it was created on the running board: /etc is an overlay whose upper layer is under /run, so a unit written there is gone on reboot and the container never restarts, with no journal entry pointing at the cause because the unit that would have logged it is what disappeared. Ship the unit in the config confext with its WantedBy symlink and the result is the one the feature promises: the board comes back and the container is running the version that was hot-reloaded before the reboot. Give the unit an ExecStartPre and a TimeoutStopSec too. The agent restarts it to adopt every pulled image, so the container's stop is on the critical path of the whole loop, and the two defaults conspire: a container that ignores SIGTERM costs systemd's full 90s stop timeout per sync, then survives the kill holding its name, so every later start fails and Restart=always retries it forever. Both numbers here are measured on the board - 90s per adopt before, 9s after - and the restart loop is one this note would otherwise have walked readers straight into. Signed-off-by: Javier Tia <floss@jetm.me>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 11 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- src/package-lock.json: Generated file
Suppressed comments (1)
src/docs-guides/container-dev-mode.md:135
- The guide refers to the device agent unit as
avocado-container-agent-dev, but the new field note usescontainer-agent-dev.service. This inconsistency will confuse readers trying to inspect logs; align the unit name here to match the documented service name elsewhere in this PR.
journalctl -u avocado-container-agent-dev -f # pull + restart, as the agent sees it
The power-cycle section carried only a one-line editor comment naming the recording, so a reader saw nothing and whoever publishes the note had no placeholder to replace - unlike the first recording, which already has the admonition plus the embed snippet to swap in. Give the second recording the same treatment, and say why it is worth watching rather than reading: the two things that prove the claim are the boot id and the container id both changing across the reboot, which is exactly what prose flattens into an assertion the reader has to take on trust. Signed-off-by: Javier Tia <floss@jetm.me>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 11 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- src/package-lock.json: Generated file
Suppressed comments (2)
src/docs-guides/container-dev-mode.md:135
- The guide names the agent as
container-agent-dev(e.g., in the table above), but this command usesavocado-container-agent-dev. Please make the unit name consistent so readers know which service to follow.
journalctl -u avocado-container-agent-dev -f # pull + restart, as the agent sees it
src/docs-guides/container-dev-mode.md:240
- This says
prunecan run without stopping the session, but the companion field note saysprunerefuses while anupsession is live. Please clarify the docs so they don't contradict each other (e.g., note thatprunemay refuse during an active session / while a device is mid-pull).
`status` and `prune` are read-mostly and safe to run whenever. `prune` does not
need the session stopped first - it refuses to sweep a blob a device is still
pulling rather than racing it.
Problem
Container Dev Mode adds a new CLI surface (a
container_devconfig block, fiveavocado container devsubcommands) and a device-side agent extension, and nothingin the docs told a user how to use any of it. The only written material was a field
note about how the push path was validated internally, which is a QA story rather
than a how-to: a reader learned how the registry was tested, not what the feature
does for them.
Solution
Split the two jobs. A new guide under Development documents the feature for an
engineer who wants to use it, written against the implementation on the
container-dev-modebranches so the commands, config keys, ports and prerequisitesare the real ones. The field note is reframed to lead with the loop a developer
already lives in - a multi-gigabyte inference container where one iteration costs
the size of the image rather than the size of the change - and links to the guide
instead of restating it. The QEMU lab and the two bugs it caught become the
credibility half rather than the premise.
Key changes
docs-guides/container-dev-mode.md: new guide (prerequisites, config block, theloop, and reference tables for commands, env vars and default ports), plus its
sidebars-guides.jsentry next to Hardware in the loop.described our own build infrastructure rather than the feature.
session lifecycle, and one per stage of
up/ iterate / teardown. This registers@docusaurus/theme-mermaidas a theme (pinned to core's version), enablesmarkdown.mermaid, and sets a light/dark diagram palette indocusaurus.config.js. It adds three direct dependencies:@docusaurus/theme-mermaidfor rendering, and
mermaidplusjsdomfor the validator below. Host and targetare distinguished by colour as well as by label, using translucent fills so both
light and dark mode stay legible.
src/scripts/check-mermaid.mjs(~230 lines), which parses every ```mermaidblock and self-tests its own scanner, wired into both
scripts/checks.shandthe `build-and-deploy.yml` workflow. Mermaid parses in the reader's browser, so a
malformed diagram survives a green `npm run build` and would ship an error box on
the published page; this validator is what makes a bad diagram fail before deploy.
5601as the write listener.There is no fixed write port in the normal case: it binds an ephemeral loopback
port that changes every session, and
5601applies only on theavocado-vmpush path, where the guest's per-registry trust store is keyed on it.
{/* ... */}comment to underscores, producing
{/_ ... _/}, which MDX fails to parse("Could not parse expression with acorn"). The pre-release note moved into
frontmatter YAML comments, which prettier preserves.
Dependencies
This PR must not merge until every item below has landed. The guide documents
commands, config keys and prerequisites that do not exist in any released build
until they do, and merging early would publish documentation for a feature a reader
cannot use.
avocado container dev up/sync/status/down/prunecommands, theruntimes.<name>.container_devblock, the embedded registry, the engine watcher, and theAVOCADO_CONTAINER_DEV_*variables.avocado-ext-container-agent-devextension the guide lists as a device prerequisite. Without it the device has nothing to pull the layer or restart the service.AVOCADO_CONTAINER_DEV_VM.<TestStatus>, replacing the pre-release1.0.0-rc.1.All three PRs are open from their own
container-dev-modebranch; #184 and #46target
main, #246 targetsscarthgap. The CLI and the agent are two halves of oneprotocol, so neither is optional and there is no useful ordering between them; the
release is necessarily last.
Tracked in ENG-2224, which carries the ship-day checklist for this PR.
Reviewer notes
This PR is a draft on purpose, and both documents are draft-gated: the field
note (
src/field-notes/2026-07-23-container-dev-mode-vm-push.mdx:5) and the guide(
src/docs-guides/container-dev-mode.md:6) each carrydraft: true, matching thePRE-RELEASE comments in their own frontmatter. Merging therefore publishes neither
page to docs.peridio.com. That is the intended state while the dependencies above
are outstanding, because the guide documents subcommands no released CLI has.
Verification. The site is npm-driven (
scripts/checks.shrunsnpm ci, and theroot
package.jsondelegates tosrcvianpm --prefix src). Run fromsrc/athead
7364928on Node v26.4.0:npm exec -- prettier --check package.json package-lock.json: "All matched filesuse Prettier code style!"
npm run check-mermaid:self-test ok: scanner agrees on 6 fence shapes,self-test ok: 3 valid accepted, 3 malformed rejected, thenmermaid ok: 5 mermaid block(s) in 175 markdown file(s).npm run lint(eslint): exit 0, no output.npm run build: exit 0,[SUCCESS] Generated static files in "build".find build -iname '*container-dev-mode*'and-iname '*vm-push*'each return nothing, so neitherthe guide nor the field note is emitted. Docusaurus records the guide as
draftIds:["container-dev-mode"]in the client bundle, and thesidebars-guides.jsentry therefore produces no nav item and no dangling link.Sibling guides do emit (
build/developer-reference/provisioning.html), which isthe path
container-dev-mode.htmlwill take once the flag is dropped.Verified against a pre-release build, not a released one. The documented
surface was read from the implementation (
src/commands/container/{mod,dev}.rs,src/utils/container_dev/config.rs,bootstrap.rsand the extension'savocado.yaml) and has since been exercised end to end against a real Avocado OSqemux86-64target: the commands, the config block, the loop and the teardown allran, on a target reached only over SSH. What remains unverified is the released
CLI, since the pin is still the pre-release
1.0.0-rc.1. Every command, config keyand port needs re-checking against the shipped binary before this merges, and the
note still carries two
[ENGINEER: ...]gaps (the<TestStatus>reference link andthe Reproduce it section) that are release-blocked.