Add container dev mode guide and field note - #473
Conversation
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.
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.
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.
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.
There was a problem hiding this comment.
🟢 Ready to approve
Only minor documentation consistency issues were found (no functional or build-blocking problems identified in the reviewed changes).
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 link includes a
.mdextension and uses the acronymHITL, but the docs-guides corpus uses extensionless relative links (e.g.custom-kernel.mdlinks./hardware-in-the-loop) and this page otherwise usesHIL. Dropping the extension keeps links stable and avoids inconsistent terminology.
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.
jetm
left a comment
There was a problem hiding this comment.
Re-checked the handoff items against 0f14b4c. One of the eight landed and is now resolved: the config-reference table is fully qualified on all four rows.
The rest are still open, and I want to be precise about why, because the two halves are different. Item 1 (the guide has no draft/unlisted while its own companion note is gated) is a decision I flagged as decide-first and it has not been made yet, so leaving it untouched was correct. Items 2 through 6 were not decisions - avocado-dev is still undefined at :137 with zero occurrences of avocado-ext-dev in the file, the stale-token sentence is still there, and the three [ENGINEER: ...] strings are unchanged. Those are still live.
One new finding inline: this round's added content brought in a fresh .md link, so the link fix and a new instance of it landed in the same push.
On Copilot's approval above - it reads "no functional or build-blocking problems identified". Two of the open findings contradict that: a reader who pastes the avocado.yaml block gets Extension 'avocado-dev' not found in configuration from avocado build, and the page as it stands publishes live for a CLI subcommand that exists on no released build. Not a complaint about the bot, just flagging that its verdict should not be read as clearing those two.
There was a problem hiding this comment.
🟡 Not ready to approve
There’s a confirmed docs-guides link/convention inconsistency to fix, and the PR description indicates the content still needs release verification/pinning before it should merge.
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 relative doc link is the only one under
src/docs-guides/that includes the.mdextension (the rest use extensionless links, e.g.custom-kernel.md:309links./hardware-in-the-loop). Also, this sentence usesHITLwhile the rest of the page usesHIL, which is likely an accidental inconsistency.
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.
There was a problem hiding this comment.
🟡 Not ready to approve
The new guide has a confirmed docs-link inconsistency (extensionful relative link) and inconsistent HITL/HIL terminology that should be standardized before publishing.
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 (2)
src/docs-guides/container-dev-mode.md:99
- This relative doc link includes the
.mdextension (./hardware-in-the-loop.md). Elsewhere in docs-guides, relative doc links are extensionless, and this page also links to the same doc without the extension earlier; keeping it extensionless avoids breaking links if the target ever changes extension and keeps the guide internally 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.
src/docs-guides/container-dev-mode.md:71
- This guide introduces the abbreviation “HIL” (e.g., “HIL target”), but the existing Hardware-in-the-loop documentation consistently uses “HITL”. Mixing abbreviations can confuse readers and makes searching across the docs harder; consider standardizing on “HITL” throughout this guide (and keeping “HIL” only if it’s already an established term elsewhere).
## Two machines: your host and the HIL target
- 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>
The default-ports table listed 5601 as the write listener alongside the two real fixed ports, so a reader would reasonably firewall it, document it, or try to reach it. There is no such port in the normal case: the write listener binds an ephemeral loopback port that changes every session, and its address is deliberately never disclosed to the device. Observed values across four sessions were 33687, 34813, 37633 and 41753. 5601 is only correct when the port is pinned, which is required just for the avocado-vm push path - there the guest's per-registry trust store and the pushed tag are both keyed on the port, so it cannot be ephemeral. That makes it a property of one topology rather than a default, so it now reads as an override with the reason attached, and the table says how to learn the real port from what `up` prints. Also narrows the `sync` advice. It said to use `sync` after a BuildKit build, which is no longer true for any current daemon and told users to work around a problem they do not have. The two cases that genuinely need it are a cross-arch buildx build, which emits no tag event at any daemon version, and a BuildKit build on a pre-23 daemon. Verified with a full site build. Signed-off-by: Javier Tia <javier@peridio.com>
The guide documented each of the five subcommands in its own section but never showed them as one arc, so a reader could follow every step and still not know which commands are one-shot against a long-lived session, what persists across a teardown, or why `prune` is safe to run at any point. The existing diagram covers the edit-build-reload idea, not the session. Split across four rather than one, because a single figure covering up through prune either loses the host/target split or grows past a readable width. The first is command-level and fits on a screen; the three that follow expand one stage each, and only those need the two-column host and target layout. The stage figures carry the details that change what a reader does: which two things the device is deliberately never told and why a compromised device therefore cannot push; that the digest on the wire is the registry manifest digest rather than a local image ID; that the agent re-tags a digest pull because it lands untagged; that the owning service is restarted rather than the container, since an engine restart re-runs the image ID pinned at create time. The teardown figure states what survives on each side, because "the container keeps running the last image it was given" is the part people assume wrongly. Keeps the existing ASCII-in-a-text-block convention and its highlight-colour markers rather than introducing Mermaid, which this site does not enable. Verified with a full site build, and by checking the rendered HTML contains the figure content with no highlight markers leaked - a marker line sharing a line with diagram text silently drops that text, which caught one line during authoring. Signed-off-by: Javier Tia <javier@peridio.com>
Diagrams here are hand-drawn ASCII in text blocks, which means every arrow and column is aligned by hand. That caps a two-column figure at roughly 84 characters, and it has already lost content once: a highlight-colour marker sharing a line with diagram text silently drops that text, because the marker line is stripped whole. Pinned to 3.10.1 to match @docusaurus/core exactly rather than the ^3.10.1 that npm resolves to 3.10.2. A theme package on a different version than core is the usual source of Docusaurus theme skew, and every sibling package here is already on 3.10.1. themeConfig.mermaid maps light and dark palettes because the colour-mode switch is user-visible; without it a diagram keeps its light palette in dark mode and renders dark text on a dark background. One property worth knowing before relying on this: mermaid parses in the browser, so a malformed diagram does NOT fail the build the way onBrokenLinks and onBrokenAnchors do - it renders an error box on the page instead. A green build stops being sufficient evidence for a diagram change; the rendered page has to be checked. That is noted at the config key rather than left for someone to discover. Also worth recording: adding this dependency incrementally on top of an existing tree produced a broken node_modules layout that hoisted an older gray-matter to the top level, whose parse.js calls the safeLoad that js-yaml 4 removed, failing every front-matter parse in the site. A clean install from the lockfile resolves it and builds fine. After changing dependencies here, reinstall clean before trusting a build. Signed-off-by: Javier Tia <javier@peridio.com>
All five figures on the page, not only the four lifecycle ones. Converting just the new figures would have left the page with one hand-drawn ASCII diagram above four mermaid ones, and a reader meets that inconsistency on a single screen. The three ordered ones become sequence diagrams, which is what they always were: `up`, one iteration, and teardown are each a conversation between the host and the target, and the ASCII versions were emulating swimlanes with hand-aligned columns. Splitting stage 2 into five participants - host engine, watcher, registry, target agent, target systemd - says which component acts at each step, which the two-column ASCII could not express at all. No hardcoded node colours. The colour-mode switch is user-visible, and a literal fill overrides the theme, so a light palette would survive into dark mode. The host and target split is carried by subgraph and participant names instead, which also reads correctly in a screen reader and does not depend on distinguishing green from blue. Net 62 lines shorter, and no arrow or column is aligned by hand any more. One consequence to know about: mermaid renders in the browser, so the figure text now lives in a JS chunk rather than in the server-rendered HTML, where the ASCII was. A reader without JS sees no diagram, and a crawler that does not execute JS will not index the figure text. `copy_markdown` still yields the mermaid source, so copying the page for an LLM keeps the diagrams. Verified by parsing every block with mermaid 11.16.0 itself, since a green build does not cover this - and by checking that the same validator rejects a bogus diagram type, an unclosed subgraph and garbage, so a pass means something. Signed-off-by: Javier Tia <javier@peridio.com>
Enabling mermaid gave up a property this repo otherwise holds: the build is the guarantee. onBrokenLinks and onBrokenAnchors both throw, but mermaid parses in the reader's browser, so a malformed diagram survives a green build and renders an error box on the published page. Pre-rendering to SVG would have restored that for free, at the cost of Chromium in CI - this restores it without one. Wired into scripts/checks.sh ahead of the build, since that is what gates pull requests, and into the build-and-deploy workflow, since that gates the deploy. Both run it before building rather than after, so a bad diagram fails fast. The check self-tests before judging anything. A validator that quietly stops discriminating is worse than none, because it reports success forever, so it asserts one valid diagram is accepted and three malformed ones - unknown diagram type, unclosed subgraph, and garbage - are rejected, and exits 2 if that assumption has drifted. Confirmed by corrupting a real diagram: the check exits 1 and names the file and line. Also fixes table padding in the guide that the earlier write-port change widened. prettier --check is part of the same gate and was failing on it, which the Docusaurus build alone did not reveal. Signed-off-by: Javier Tia <javier@peridio.com>
…runs on The mermaid conversion dropped machine attribution from exactly the steps a reader most needs it for. "edit your app", "docker build -t my-app:dev ." and "watch it run on real hardware" sat outside both subgraphs, and the transfer edge said only "only the changed layer" - so the four elements carrying no host-or-target label were the four a newcomer would ask about first. The ASCII original did carry it, through a third highlight colour marking the you-and-your-build zone. Subgraphs replaced the colours in the conversion, but only two were created, so those nodes had nowhere to live and floated. Every node now sits in a labelled subgraph, and the two host subgraphs both say so explicitly rather than leaving the reader to infer that the watcher and registry are also local - a point people get wrong in the other direction, assuming the registry lives on the device. The transfer edge names its direction. The lifecycle figure had the same defect for a different reason: all five commands run on the host, which the figure never stated because nothing in it contrasted with anything. Wrapping it says so once. Signed-off-by: Javier Tia <javier@peridio.com>
An uppercase word in a group label was carrying the single most important distinction on the page. It is easy to skim past, and on the sequence diagrams it was doing the work alone. Blue now means your host and amber means the HIL target, in every figure, with the one arrow that crosses the network thickened in amber so the single network hop is the most visible edge in the diagram. Blue and amber rather than the more obvious green and red: that pair stays distinguishable under the common forms of colour blindness. The labels are kept as well, so nothing depends on colour alone and the figures survive greyscale. The fills carry an alpha channel deliberately. A solid light fill looks right until a reader flips to dark mode, at which point the theme draws light text onto it and the label disappears; a tint lets the page background through, so both themes stay legible. That is also why the earlier conversion shipped with no colour at all - this gets the distinction without reintroducing the problem that argued against it. Sequence diagrams group participants with `box`, which is what makes the lanes readable at a glance on the five-participant one. check-mermaid gets a fix that this change depends on: it promoted only window, document and navigator into the global scope, but parts of mermaid reference browser constructors bare - a sequence `box` reaches `Option` - so every diagram using one failed with "Option is not defined", which is indistinguishable from a syntax error. The check would have rejected all three sequence diagrams below. It now promotes the whole window surface, and its self-test pins a styled `box` diagram as a must-accept case so the gap cannot reopen silently. Signed-off-by: Javier Tia <javier@peridio.com>
The group labels were set in capitals back when an uppercase word was the only thing separating host from target. Colour carries that now, so the capitals are just volume, and readers hear them as shouting. Sentence case also matches the headings on this page, which are all sentence case. Same treatment inside the figures, where words like ONCE and UNTAGGED were capitalised for emphasis. The surrounding prose already makes those points, so the emphasis was redundant as well as loud. Genuine acronyms are left alone. Also replaces eight em dashes with hyphens. The file had none before this session; every one was mine, so this is restoring the file's own punctuation rather than imposing a preference on it. Signed-off-by: Javier Tia <javier@peridio.com>
…ve site The guide had no draft flag and a sidebar entry, so merging published it, while the field note in the same change carried draft:true with the reasoning "a reader cannot reproduce this today". That reasoning applies to the guide word for word: every `avocado container dev` subcommand on the page exists only on avocado-cli's unmerged container-dev-mode branch, so a reader would have followed the guide into a subcommand no released CLI has. A production build now emits nothing for either page. The sidebar entry stays. Docusaurus resolves a draft doc into `draftIds` and drops it from the sidebar itself rather than failing the build, which I checked both ways - so removing the entry would only add a second line someone has to remember to put back. Three claims on the page were wrong independently of the gating. `avocado-dev` is undefined here: hardware-in-the-loop.md can use that name because it defines it inline, and this page copied the usage without the definition, so `avocado build` would fail to resolve it. `avocado-ext-dev` is the prebuilt that needs no definition. The `status` paragraph promised a stale-token warning the CLI does not emit, which is worse than saying nothing - a reader whose token is stale sees a clean `status` and concludes the session is healthy while pulls fail. And the hardware-in-the-loop link kept a `.md` extension no other guide link uses, which survives only until that page becomes `.mdx`. The field note's two placeholders are handled differently from each other. The `reference` prop renders as a link's anchor text, and neither the dev server nor a deploy preview honours draft:true, so a placeholder there ships a link reading like a drafting note; the prop is dropped until the reference code is public. The "Reproduce it" placeholder stays, because it is the real remaining gap - but the frontmatter comment no longer spells the bracketed marker, so the pre-merge grep in CONTRIBUTING.md now matches that one gap instead of matching this file forever and training the next person to ignore the gate. Signed-off-by: Javier Tia <javier@peridio.com>
Two fence shapes walked straight past the scanner, and both failed silently - the run printed "mermaid ok" and a count that looked healthy because the blocks it missed were never counted either. Matching the fence as the exact string ```mermaid skipped any block carrying metadata after the language, which Docusaurus accepts and this repo is free to start using at any time. An unterminated fence was worse: the block was dropped on the floor, so the one case where the page is definitely broken - Markdown swallows the rest of the file into a code block - was the one case CI stayed green for. The scanner is split out from the file read so it can be self-tested on fixture strings, which the existing self-test structurally cannot cover: that one feeds known-bad diagrams to mermaid's parser, and a block the scanner never yields is never handed to the parser at all. A scanner that silently found nothing would report "0 blocks, ok" forever, which is the exact failure this script's own header argues against. Signed-off-by: Javier Tia <javier@peridio.com>
check-mermaid.mjs imports mermaid directly at line 195, but package.json declared only @docusaurus/theme-mermaid. The import resolved by hoisting: theme-mermaid requires mermaid at ">=11.6.0", npm flattened 11.16.0 to node_modules/mermaid, and the bare specifier happened to find it. That range is open-ended, so a theme-mermaid bump can move mermaid across a major boundary, and a nested install would remove the hoisted copy outright. Either way the validator is what breaks, in CI, where it exists to catch bad diagrams - so a mermaid API change would surface as "validator crashed" rather than "diagram invalid", pointing at the wrong thing. Declaring it directly pins the major this code is written against and makes the dependency visible to anyone reading package.json, matching how jsdom is already handled. Signed-off-by: Javier Tia <javier@peridio.com>
The fence scanner recognised only a three-backtick opener at column 0 closed by a bare three-backtick line. Docusaurus is not that narrow: its mermaid transform takes any mdast code node whose lang is mermaid, so indented fences inside a list item, tilde fences, and fences longer than three characters all render for readers while this gate dropped them silently. A three-backtick block closed by a four-backtick fence was worse - CommonMark says that closes it, the scanner did not, so it reported an unterminated fence and stopped scanning the rest of the file. Each of those is the silent-green failure the header comment says the script exists to prevent. Parse to mdast and select code nodes with lang mermaid instead. Matching the parser rather than re-deriving fence rules also settles the two cases the old scanner got wrong in the other direction, without special-casing either: a fence inside an HTML comment belongs to an html node and is inert exactly as it is for a reader, and a mermaid fence nested in a longer fence - the correct way to SHOW mermaid source - is part of the outer code node, so documenting the convention with a deliberately-bad example no longer fails CI. parse() is only a syntax check, so a diagram that parses and then throws while rendering still shipped the error box this gate exists to stop. Render each block too. That needs SVG measurement, which jsdom does not implement, so getBBox and friends are stubbed with fixed numbers - failures that depend on real text metrics stay out of reach, which is stated where the stubs are. A gantt with an unparseable task date is the case that proves the step: parse accepts it, render rejects it, and it is now a self-test. Failures used to print only the first line of mermaid's message, discarding the caret, the offending token and the Expecting list, and printed mermaid's body-relative line number against the fence line - naming a line the author did not write. The whole message is kept and its line numbers are rebased onto the file. Two floors, because reporting success over nothing checked is the failure this script is supposed to rule out: no markdown files found, or files found with not one diagram among them, now exit non-zero. A self-test covers the file walk, which the synthetic scanner cases never touched. The DOM promotion skipped every name already on Node's globalThis, which is exactly the DOM constructors that matter - Event, EventTarget, DOMException, Blob, URL, crypto and the rest - leaving mermaid on Node's realm while the document was jsdom's. Those are now forced, so dispatchEvent cannot fail with "parameter 1 is not of type 'Event'" and get reported as the author's diagram being invalid. Finally, generated reference pages are written into a gitignored directory by sync-references, which the build runs - so checking before the build scanned the tree while that whole set of live pages was absent. checks.sh and the workflow now generate them first, and the script warns when the directory is missing rather than letting the total imply full coverage. It reports 203 files where it used to report 175. mdast-util-from-markdown and unist-util-visit are declared directly rather than used via hoisting, for the same reason mermaid now is. Signed-off-by: Javier Tia <javier@peridio.com>
Two diagrams contradicted the text beside them, and both mislead a reader who is debugging rather than reading straight through. The first figure drew one arrow from the control component to the agent labelled "only the changed layer", and the legend called it the one hop that crosses the network. The rest of the page says otherwise: the CLI notifies the device on the control port and the agent then pulls the layers back from the host's registry on the bulk port, which the ports table gives as 5599. A reader who trusts the figure opens only the control port through a firewall or a QEMU port-forward and the pull fails with nothing in the figure to explain it. Draw both hops with their ports, and say in the legend that there are two and that opening only the control port is the usual reason a sync appears to stall. The cycle figure's only edge into prune came from down, sequencing it strictly after teardown. Three lines below, the prose says prune does not need the session stopped because it refuses to sweep a blob a device is still pulling, and the node itself is labelled "at any point". So the figure told a reader who is low on disk mid-session to give up a live loop they could have kept. Add the edge from the live loop and make the optional ones dotted; the Stage 3 heading said "down, then prune" and now does not imply that order either. Also records why the two draft flips are coupled. The note links /developer-reference/container-dev-mode, which is not a route while the guide is draft, and onBrokenLinks is 'throw' - so un-drafting the note alone fails the build. The guide can publish alone; the note cannot. checks.sh runs the build on every pull request, so this surfaces on the PR rather than at deploy, but nothing stated the ordering and each file's comment described its own flip as independent. Signed-off-by: Javier Tia <javier@peridio.com>
…om its port The page described the write listener as loopback-only AND ephemeral AND never disclosed to the device, then documented AVOCADO_CONTAINER_DEV_WRITE_PORT for pushing through an engine guest that has to know the port. Read together those cannot all hold, and a reader cannot tell which claim the VM path breaks. The implementation separates the two properties: avocado-cli binds 127.0.0.1 either way and only the port varies - a known port when VM routing is active, an ephemeral one otherwise. So loopback-only is unconditionally true and it is "ephemeral" and "never disclosed" that are specific to the default path. Say exactly that, and say why loopback still holds on the VM path: the guest reaches the listener through the forwarded socket rather than over the network, so a fixed port is not a wider exposure. The ports table gets the same qualification, since reading it alone was enough to draw the wrong conclusion. Signed-off-by: Javier Tia <javier@peridio.com>
…rget Two things a reader on real hardware hits that the qemu lab never surfaces, both found running the loop against an arm64 board. The prerequisites listed two extensions and left out the SSH server. `up` delivers the session bootstrap over SSH before the loop exists - only steady-state syncs use the control WebSocket - so a target without an SSH server cannot be bootstrapped at all. A minimal Avocado image ships none, so the omission reads as "my board is fine, the tool is broken". Nothing addressed an x86-64 host driving an arm64 board, which is the ordinary case for anyone with hardware on their desk. A plain docker build produces an image the target cannot execute, and the device-side pull refuses it rather than shipping it, so the failure lands at sync time. The fix is --platform, and it costs a second problem: it routes the build through buildx, so the BuildKit caution immediately above applies for a second reason and the watcher may not see the rebuild at all. The two interact, so the cross-build example pairs with the explicit sync rather than leaving the reader to rediscover that. Signed-off-by: Javier Tia <javier@peridio.com>
The VM-push note proves the path in a QEMU lab, which is the right way to find those bugs before hardware exists, and then offers nothing to someone holding a board - the readers most likely to act on it. This is that reader's note: an NXP FRDM i.MX 93 driven from an x86-64 workstation, from a blank SD card to a code change serving off the device. It is deliberately its own note rather than a section in the other one. The two are not two views of one system - the VM push path is the macOS topology, where the engine runs in a guest reaching the host at 10.0.2.2 over authenticated HTTPS, while the board loop is native Linux, where docker's loopback exemption means no certificate configuration at all. Neither bug the VM note documents is reachable on the board path. The steps lead with the three places a quiet failure waits, because each reports success while doing nothing: a unit outside overlay/usr/lib/systemd/system is never enabled, a wrong-architecture image fails at sync rather than at runtime, and a missing SSH server presents as a container problem rather than the bootstrap failure it is. It carries an internal section listing the PRs the loop is currently spread across, so it can be reproduced before any of them merge, and a note that the board ran a wrynose build rather than the published BSP - that move is held pending an open silicon-revision question, so a reviewer's board may behave differently. Both are marked to strip before the note publishes, and it stays draft behind the same gate as the guide.
…at to expect The note had the steps right and left three things to inference: which machine a command belongs on, how long a step should sit there before you start suspecting it, and what a successful result looks like as a number. Two machines with an ssh hop between them is the setup where a command run in the wrong place fails in a way that reads as the feature being broken, so the reader should never have to work that out. Each heading now names its machine, the one genuinely long step is marked as the only one, and the transfer figures are given as something to measure rather than to accept. The registry writes each blob once, so the growth of its store across a sync is exactly what crossed to the device and the reader can confirm the headline claim themselves. Verification previously rested on a single curl, which reports what is serving now and not that anything changed. Streaming the unit's log across the rebuild separates those two. A second pass then gets its own section, because it is not a clean repeat of the first: a live session blocks the next up, leftover blobs make a re-measured first delivery collapse to almost nothing, and repeated agent restarts trip systemd's start rate limit. Signed-off-by: Javier Tia <javier@peridio.com>
…ote renders
The internal section was wrapped in `{/_ ... _/}`, which is not an MDX
comment, so the file failed MDX compilation with "could not parse
expression with acorn". Nothing caught it: the note carries draft:true,
the production build excludes drafts, and `npm run build` therefore
passed while the page could not render at all. The dev server and a
deploy preview do render drafts, which makes this fail in exactly the
two places a draft gets reviewed.
The delimiters were almost certainly not typed that way. Spread over two
lines the `*` pair reads as emphasis to a markdown formatter, which
normalises emphasis to `_` and silently converts a working comment into
a broken expression. Keeping the comment on a single line removes the
span the heuristic needs, and the comment now says so, since the next
formatter run would otherwise reintroduce it.
Signed-off-by: Javier Tia <javier@peridio.com>
The recording exists and has been reviewed, but publishing it is a separate step, and a slot that only lives in someone's head gets filled late or not at all. Reserving it in the note makes the gap visible to a reviewer and gives whoever un-drafts this a block to paste rather than markup to reconstruct. It is an admonition rather than a commented-out iframe so it is visible during review, and it carries the embed itself so the fill-in is a substitution instead of a rewrite. The aspect ratio is the part worth writing down. A terminal recording is close to square - this one is 1188x1152 - and the 16/9 wrapper the other field notes use would letterbox it to a stripe. The placeholder pins the real ratio and says why, because 16/9 is the obvious thing to copy from the neighbouring notes and it is wrong here. Signed-off-by: Javier Tia <javier@peridio.com>
…le the demo app Without an `image`, the note shares as a bare title card, which for a result this visual wastes the one frame that carries the whole argument. The card is the LANDED frame: the reloaded log line, and 2.5 KB against 1.9 MB. It is a crop rather than a whole frame. The recording is a terminal and so nearly square, while a social card is landscape - dropped in whole it would letterbox to a stripe. Cropping to the payoff region lands at 1188x648, the same 1.83:1 the other field-note cards use, and does it at native resolution so the text stays sharp instead of upscaling into a 1320x720 slot. The recording and the steps also use different example apps, which is the kind of small mismatch a reader hits and then wonders which of the two they got wrong. Said plainly instead: the filmed app prints a line so the reload is visible as a stream, the written one serves HTTP because that is the more useful shape to copy, and the loop does not care which. Signed-off-by: Javier Tia <javier@peridio.com>
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>
…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>
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>
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>
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>
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 (1)
src/docs-guides/container-dev-mode.md:135
- The unit name in this
journalctlexample looks inconsistent with the rest of the docs in this PR (which refer to the agent ascontainer-agent-dev, e.g. the field note usescontainer-agent-dev.service). If the actual systemd unit iscontainer-agent-dev.service, this command will fail as written.
journalctl -u avocado-container-agent-dev -f # pull + restart, as the agent sees it
nicksinas
left a comment
There was a problem hiding this comment.
Review pass over the guide, both field notes, and the mermaid gate. Verified locally at head 06d128bb: npm ci, npm run check-mermaid (5 blocks / 189 files, 1.2s), npm run build (exit 0), prettier clean. Inline comments follow.
| The fix is to ship the unit in an extension rather than create it on the device, | ||
| which is what the `config` confext in step 2 is for. Ship the `WantedBy` symlink | ||
| alongside it rather than relying on `enable_services` alone: | ||
|
|
||
| ```text | ||
| etc/systemd/system/app.service | ||
| etc/systemd/system/multi-user.target.wants/app.service -> /etc/systemd/system/app.service | ||
| ``` |
There was a problem hiding this comment.
This contradicts step 2. Line 176 says the unit goes at overlay/usr/lib/systemd/system/my-app.service — "that exact path, because it is where enable_services looks for it" — and warns that anywhere else leaves it disabled forever. Here the fix is etc/systemd/system/app.service plus a hand-written multi-user.target.wants symlink, "rather than relying on enable_services alone."
Two incompatible instructions for the same file, ~220 lines apart. A reader who followed step 2 has no way to act on this section. Either step 2's path is wrong for surviving a reboot (in which case step 2 needs fixing, not a second recipe here), or this section is describing a setup step 2 doesn't produce.
| ```text | ||
| uptime: up 1 min | ||
| app.service: active | ||
| avocado-dev my-app:dev Up 34 seconds |
There was a problem hiding this comment.
This whole section uses a different naming scheme from the reproducer above it:
- L396 cites "the
configconfext in step 2" — step 2 declares one extension, namedmy-app. There is noconfig. - Unit is
app.servicehere,my-app.servicein step 2. - Container is
avocado-devhere (L408, L409, and thisdocker psoutput),my-appin step 2 (L184-185).
Reads as lifted from a different reproducer and not reconciled. Since this is the section proving persistence across a power cycle — the claim a reader is most likely to want to repeat — the identifiers need to match step 2 exactly.
|
|
||
| - **Blue** - on **your host**: you, your build, and everything `avocado container dev up` starts. | ||
| - **Amber** - on the **HIL target**: the agent, the target's own engine, and your service. | ||
| - A **thick amber arrow** crosses the network. There are two such hops, and they |
There was a problem hiding this comment.
The legend promises two thick amber arrows; the diagram styles one.
Link indices in declaration order: edit-->build 0, watcher-->registry 1, registry-->control 2, agent-->engine 3, engine-->unit 4, unit-->running 5, build-->watcher 6, control-->agent 7, registry-->agent 8, running-->edit 9. L97 styles only index 7 (the notify hop). The bulk-pull edge (index 8) — the second network hop this callout tells readers to look for — is unstyled.
This is the reading the callout asks the reader to carry, and it's also the diagnostic it offers ("Opening only the control port is the usual reason a sync appears to stall"), so it's worth having the figure actually show both hops. Add linkStyle 8 with the same treatment, or reword to name only the control 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 |
There was a problem hiding this comment.
Two things here.
The comment says "link 7 is the one arrow that leaves your host," but two arrows leave the host in this diagram — control-->agent (7) and registry-->agent (8) — as the callout at L38-45 states.
More durably: linkStyle indices are positional over declaration order, so inserting any edge above silently restyles the wrong one, and check-mermaid still passes because the diagram parses fine. This is the most fragile line in the new figures and it's outside what the new gate can see, which is worth noting given the gate's stated purpose. A classDef applied to the target nodes, or linkStyle default plus explicit styling of the two crossings, both survive edits; a hardcoded index does not.
|
|
||
| This is scoped to the container dev mode store and is distinct from the top-level `avocado clean`, which removes Docker volumes and project state. | ||
|
|
||
| ## Command reference |
There was a problem hiding this comment.
docs-guides/avocado-cli/commands.md is the canonical per-command reference and enumerates every family — avocado ext *, avocado runtime *, avocado connect *, and the top-level commands. These five subcommands land only in this guide, so a reader on the reference page gets no signal that avocado container dev exists.
Worth adding to commands.md in the same change that un-drafts this page, so the reference doesn't silently become incomplete.
| // highlight-orange-start | ||
| edit your app <-- you | ||
| │ | ||
| ▼ | ||
| docker build -t my-app:dev . <-- your normal build | ||
| │ | ||
| ▼ | ||
| // highlight-orange-end | ||
| // highlight-green-start | ||
| avocado container dev <-- started by dev up | ||
| ┌─────────────────────────┐ | ||
| │ watcher sees the tag │ | ||
| │ registry keeps layers │ | ||
| │ control notifies │ | ||
| └─────────────────────────┘ | ||
| │ | ||
| │ only the changed layer | ||
| ▼ | ||
| // highlight-green-end | ||
| // highlight-blue-start | ||
| the device (QEMU or a board) <-- hardware in the loop | ||
| ┌─────────────────────────┐ | ||
| │ container-agent-dev │ pulls over the pinned CA | ||
| │ engine my-app:dev │ changed layer applied | ||
| │ systemd restart app │ service back up | ||
| └─────────────────────────┘ | ||
| │ | ||
| ▼ | ||
| watch it run on real hardware | ||
| // highlight-blue-end |
There was a problem hiding this comment.
Colour semantics invert against the guide. docs-guides/container-dev-mode.md:38-45 declares "Blue — on your host … Amber — on the HIL target," and says the convention holds "everywhere on this page." Here the same architecture uses orange for you, green for the host-side CLI, and blue for the device.
So blue means host on one page and target on the other, for the same three-box loop, and the two pages link to each other. Since the guide asks readers to learn the colour key, it's worth having this figure follow it — or dropping the colour blocks here and leaning on the labels.
| "file-loader": "^6.2.0", | ||
| "jsdom": "^26.1.0", | ||
| "mdast-util-from-markdown": "^2.0.2", | ||
| "mermaid": "^11.16.0", |
There was a problem hiding this comment.
@docusaurus/theme-mermaid@3.10.1 takes mermaid: ">=11.6.0" as a regular dependency, not a peer. So this direct entry isn't needed for rendering — and because npm hoists a single copy, the validator's range is currently what the site renders with too. Bumping the validator changes reader-facing output; conversely, a mermaid major bump resolves the theme to its own nested copy and the validator quietly stops validating what ships, which is the one property the script exists to guarantee.
Suggest pinning this to the theme's resolved version (or resolving mermaid through the theme's tree) so the two can't diverge.
Separately, mdast-util-from-markdown (L46) and unist-util-visit (L58) are validator-only and look like devDependencies rather than dependencies.
| - name: Sync Reference Pages | ||
| run: npm --prefix src run sync-references | ||
| # The build below cannot catch a malformed diagram - mermaid renders in the | ||
| # browser - so gate the deploy on it explicitly. | ||
| - name: Mermaid Diagrams | ||
| run: npm --prefix src run check-mermaid |
There was a problem hiding this comment.
The Sync Reference Pages step exists only because check-mermaid runs before the build — and src/scripts/build.sh:5 already runs sync-references. So each CI run now clones/pulls avocado-linux/references twice.
The deploy job is needs: build, so any failing step in this job blocks the deploy regardless of where it sits. Moving Mermaid Diagrams to after Docusaurus Build drops the duplicate step here, drops the same duplicate from scripts/checks.sh:22-26, and makes the script's absent-references warning path unreachable — with no change in what gets gated.
| function markdownFiles(dir) { | ||
| const out = [] | ||
| for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { | ||
| if (SKIP_DIRS.has(entry.name)) continue | ||
| const full = path.join(dir, entry.name) | ||
| if (entry.isDirectory()) out.push(...markdownFiles(full)) | ||
| else if (/\.mdx?$/.test(entry.name)) out.push(full) | ||
| } | ||
| return out | ||
| } | ||
|
|
There was a problem hiding this comment.
The walk includes docs-guides/references/, which scripts/sync-references.js generates by cloning avocado-linux/references at unpinned origin/main HEAD. Combined with the new CI step, that means a malformed mermaid block committed to that repo turns this repo's deploy red, with no fix available here.
The script already treats those pages as a special case (the absent-directory warning near the end) and the header comment notes they "carry upstream markdown we do not control" — so the same reasoning argues for failures in generated trees being a warning rather than a hard failure, while authored content stays fail-closed.
| * one throws "X is not defined", which is indistinguishable from a syntax error at | ||
| * the call site. That would fail a perfectly valid diagram and block it in CI. | ||
| */ | ||
| function installDom() { |
There was a problem hiding this comment.
Flagging the operational cost of this approach, not the approach — the tradeoffs are honestly documented, which helps.
installDom promotes the whole jsdom window surface onto globalThis and installSvgMetrics returns constant getBBox/getComputedTextLength values, so render() is approximate. The consequence for maintenance is that a mermaid upgrade can fail CI for harness reasons and the failure surfaces at the parses() call site — i.e. reported as the author's diagram being invalid. CROSS_REALM_GLOBALS is exactly the kind of list that goes stale silently.
Compounding it: the PR body reports verification on Node v26.4.0, but CI pins node-version: 20.19.0. The one script in the repo whose behaviour depends on which globals Node itself defines (the navigator getter note at L131-136 is precisely this) has not been run on the CI runtime. Worth a run on 20.19.0 before merge.
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.