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.
| | `runtimes.<name>.container_dev` | yes | Presence of the block enables the feature for that runtime. | | ||
| | `container_dev.images[].ref` | yes | Image reference (`repository[:tag]`) watched on the host engine. | | ||
| | `container_dev.images[].service` | yes | Device service that consumes the image and is restarted after a pull. | | ||
| | `container_dev.registry.port` | no | Port for the bulk read listener. Defaults to `5599`. | |
There was a problem hiding this comment.
Agreed. Row 1 is runtimes.<name>.container_dev and rows 2-4 drop the prefix (container_dev.images[].ref, .images[].service, .registry.port), so the table reads as if the later keys sit at the top level of the file rather than under the same runtime.
Fully qualifying all four is the fix. Queuing it with the other changes on this PR.
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.
| @@ -0,0 +1,216 @@ | |||
| --- | |||
| sidebar_position: 3 | |||
There was a problem hiding this comment.
This guide publishes to production for a feature that has not shipped, while its own companion note is gated for exactly that reason.
The frontmatter here has no draft or unlisted, and src/sidebars-guides.js:34 registers it, so merging publishes it live. The field note in this same PR carries draft: true with the comment "PRE-RELEASE feature - keep on the branch until Container Dev Mode ships" and "a reader cannot reproduce this today."
That reasoning applies to the guide verbatim. Checked the CLI: git grep container_dev in avocado-cli returns 0 files on origin/main and 15 only on the unmerged origin/container-dev-mode branch. A production build confirms the asymmetry - it emits developer-reference/container-dev-mode.html containing avocado container dev up, and emits nothing at all for the note.
So a reader lands on the live page and runs a subcommand that exists on no released CLI. Gate the guide the same way the note is gated, or hold the whole PR until the CLI lands.
| runtimes: | ||
| dev: | ||
| extensions: | ||
| - avocado-dev |
There was a problem hiding this comment.
avocado-dev is not a real extension name, and this example does not define it.
The shipped prebuilt extension is avocado-ext-dev - 67 files under src/ use that spelling. The bare avocado-dev appears in exactly one other doc, hardware-in-the-loop.md:30, and it is legal there only because that same file defines it inline at line 43 under a top-level extensions: block.
This guide copied the usage and dropped the definition, so a reader who pastes this avocado.yaml and runs avocado build gets Extension 'avocado-dev' not found in configuration (it resolves defined: false, per config_show.rs:277-286). Confirmed it reaches the rendered page: avocado-dev appears once in the built HTML, avocado-ext-dev zero times.
Either rename it to avocado-ext-dev or add the inline definition block the way hardware-in-the-loop.md does.
| avocado container dev status | ||
| ``` | ||
|
|
||
| `status` reports registry, watcher, and last-sync state. It also surfaces the case where a device presents a stale token and needs `up` to be re-run to re-bootstrap. |
There was a problem hiding this comment.
The stale-token sentence describes behavior the CLI explicitly does not have yet.
The first half of this paragraph (registry, watcher, last-sync) is accurate. The claim that status "surfaces the case where a device presents a stale token and needs up to be re-run" is not.
avocado-cli src/commands/container/dev.rs:26-36 says so in its own words: the per-device status.devices list and the needs_rebootstrap() rotation are "NOT YET LIVE, despite being implemented and tested in bootstrap.rs", because "up writes session.json once and never updates it, so devices stays empty and needs_rebootstrap() is structurally false".
A reader with a genuinely stale token runs status, sees nothing flagged, and concludes the session is healthy while pulls fail. Drop the sentence until the path is wired.
| targets={['qemux86-64']} | ||
| cli="1.0.0-rc.1" | ||
| reference={{ | ||
| label: '[ENGINEER: reference link once the code is public]', |
There was a problem hiding this comment.
This placeholder is inside a component prop, so it renders as link text rather than sitting in prose.
It is passed as reference.label to <TestStatus>, and src/components/TestStatus/index.jsx:45-48 renders that as the anchor text. The moment draft flips to false - or on any dev-server or deploy-preview render, neither of which honors draft - the card at the top of the note shows a GitHub-iconed link reading [ENGINEER: reference link once the code is public], pointing at the bare avocado-linux/references repo root.
Every sibling note points at a real /tree/main/<reference-name> subpath. This is the only placeholder in the corpus that lands in a component prop instead of body prose, which is also why a read-through of the rendered page is less likely to catch it.
|
|
||
| ## Reproduce it | ||
|
|
||
| [ENGINEER: Container Dev Mode is pre-release - fill this in once the CLI and the |
There was a problem hiding this comment.
The whole "Reproduce it" section is still an editorial placeholder.
Six lines of [ENGINEER: ...] drafting notes naming setup-lab.sh and verify-vm-write-path.sh, with no link to either, standing where the runnable instructions go.
Correctly gated today by draft: true, so this is a blocker on the un-draft rather than on the merge - flagging it so it does not get lost when the feature ships. CONTRIBUTING.md:124: "The draft only becomes 'ready' when no [ENGINEER: ...] placeholders remain."
| # PRE-RELEASE NOTE (not for readers): Container Dev Mode is not shipped yet. | ||
| # The framing and the measured results are real, but the CLI and the device | ||
| # extension are pre-release, so a reader cannot reproduce this today. It stays | ||
| # draft:true on this branch until the feature ships and the [ENGINEER: ...] |
There was a problem hiding this comment.
This frontmatter comment will trip the pre-merge placeholder gate forever.
The comment contains the literal string [ENGINEER: ...], and the documented gate at CONTRIBUTING.md:126-128 is grep -l "\[ENGINEER:" *.mdx. So even after the two real placeholders (lines 30 and 151) are filled, this file keeps matching.
The next person either hunts for a placeholder that no longer exists, or learns to ignore the gate for every note - which is the worse outcome. Rewording to something like "the ENGINEER gaps" clears it without losing the meaning.
…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.
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.
block using the repo's existing box-drawing and magic-comment colors rather than
adding a Mermaid dependency.
{/* ... */}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. The field note is
draft: trueso it is excludedfrom the production build, but the guide is not: merging publishes it to
docs.peridio.com. Draft status makes an accidental merge impossible while the
dependencies above are outstanding.
Verification. Run in
src/against thecontainer-dev-modebranches ofavocado-cli and avocado-os:
npx prettier --checkon all three files: clean.npm run lint(eslint): clean.npm run build:[SUCCESS] Generated static files in "build". Confirmeddeveloper-reference/container-dev-mode.htmlis emitted and the field note isabsent from the build, so the draft flag does what it should.
Unverified content, flagged. The guide was written by reading the implementation
on the branches above, not by running a released binary: the documented surface
comes from
src/commands/container/{mod,dev}.rs,src/utils/container_dev/config.rs,bootstrap.rsand the extension'savocado.yaml(CLI1.0.0-rc.1, a pre-releasedev build). No command output is reproduced in the guide, because none of the
commands were run against a released build. Every command, config key and port needs
re-verification against the shipped CLI before this merges, and the note still
carries two
[ENGINEER: ...]gaps (the<TestStatus>reference link and theReproduce it section) that are release-blocked.