Skip to content

Add container dev mode guide and field note - #473

Open
jetm wants to merge 38 commits into
mainfrom
field-notes/container-dev-mode-vm-push
Open

Add container dev mode guide and field note#473
jetm wants to merge 38 commits into
mainfrom
field-notes/container-dev-mode-vm-push

Conversation

@jetm

@jetm jetm commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Problem

Container Dev Mode adds a new CLI surface (a container_dev config block, five
avocado container dev subcommands) and 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 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-mode branches so the commands, config keys, ports and prerequisites
are 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, the
    loop, and reference tables for commands, env vars and default ports), plus its
    sidebars-guides.js entry next to Hardware in the loop.
  • Field note reframed around the developer's loop; drops a remote-build bullet that
    described our own build infrastructure rather than the feature.
  • Adopts Mermaid site-wide. The guide carries five diagrams: the loop, the
    session lifecycle, and one per stage of up / iterate / teardown. This registers
    @docusaurus/theme-mermaid as a theme (pinned to core's version), enables
    markdown.mermaid, and sets a light/dark diagram palette in
    docusaurus.config.js. It adds three direct dependencies: @docusaurus/theme-mermaid
    for rendering, and mermaid plus jsdom for the validator below. Host and target
    are distinguished by colour as well as by label, using translucent fills so both
    light and dark mode stay legible.
  • Adds src/scripts/check-mermaid.mjs (~230 lines), which parses every ```mermaid
    block and self-tests its own scanner, wired into both scripts/checks.sh and
    the `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.
  • Corrects the default-ports table, which listed 5601 as the write listener.
    There is no fixed write port in the normal case: it binds an ephemeral loopback
    port that changes every session, and 5601 applies only on the avocado-vm
    push path, where the guest's per-registry trust store is keyed on it.
  • Fixes a latent MDX defect: prettier rewrites the asterisks in a {/* ... */}
    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.

Blocks merge Provides what this PR documents
avocado-linux/avocado-cli#184 - Container Dev Mode: host CLI, embedded registry, and VM push path The avocado container dev up/sync/status/down/prune commands, the runtimes.<name>.container_dev block, the embedded registry, the engine watcher, and the AVOCADO_CONTAINER_DEV_* variables.
avocado-linux/avocado-os#46 - Add container-agent-dev: device-side agent + dev-only extension The avocado-ext-container-agent-dev extension the guide lists as a device prerequisite. Without it the device has nothing to pull the layer or restart the service.
avocado-linux/meta-avocado#246 - meta-avocado-qemu: provision the container-dev trust-store location The trust-store location the VM push path delivers the per-project CA into, behind AVOCADO_CONTAINER_DEV_VM.
An avocado-cli release containing the above A released version to pin in the guide and in the note's <TestStatus>, replacing the pre-release 1.0.0-rc.1.

All three PRs are open from their own container-dev-mode branch; #184 and #46
target main, #246 targets scarthgap. The CLI and the agent are two halves of one
protocol, 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 carry draft: true, matching the
PRE-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.sh runs npm ci, and the
root package.json delegates to src via npm --prefix src). Run from src/ at
head 7364928 on Node v26.4.0:

  • npm exec -- prettier --check package.json package-lock.json: "All matched files
    use 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, then
    mermaid 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".
  • Build emission checked directly rather than inferred: find build -iname '*container-dev-mode*' and -iname '*vm-push*' each return nothing, so neither
    the guide nor the field note is emitted. Docusaurus records the guide as
    draftIds:["container-dev-mode"] in the client bundle, and the
    sidebars-guides.js entry therefore produces no nav item and no dangling link.
    Sibling guides do emit (build/developer-reference/provisioning.html), which is
    the path container-dev-mode.html will take once the flag is dropped.
  • Both pages reviewed on a local dev server, which renders drafts.

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.rs and the extension's
avocado.yaml) and has since been exercised end to end against a real Avocado OS
qemux86-64 target: the commands, the config block, the loop and the teardown all
ran, 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 key
and port needs re-checking against the shipped binary before this merges, and the
note still carries two [ENGINEER: ...] gaps (the <TestStatus> reference link and
the Reproduce it section) that are release-blocked.

Copilot AI lite review requested due to automatic review settings July 30, 2026 18:48
@jetm
jetm marked this pull request as ready for review July 30, 2026 18:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Comment thread src/docs-guides/container-dev-mode.md Outdated
Comment thread src/docs-guides/container-dev-mode.md Outdated

@jetm jetm left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/docs-guides/container-dev-mode.md
Comment thread src/docs-guides/container-dev-mode.md Outdated
Comment thread src/docs-guides/container-dev-mode.md Outdated
Comment thread src/field-notes/2026-07-23-container-dev-mode-vm-push.mdx Outdated
Comment thread src/field-notes/2026-07-23-container-dev-mode-vm-push.mdx
Comment thread src/field-notes/2026-07-23-container-dev-mode-vm-push.mdx Outdated
Copilot AI review requested due to automatic review settings July 30, 2026 22:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Copilot AI review requested due to automatic review settings July 31, 2026 18:59
Copilot AI previously approved these changes Jul 31, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 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.

Copilot AI review requested due to automatic review settings July 31, 2026 22:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Copilot AI review requested due to automatic review settings July 31, 2026 23:10
Copilot AI dismissed their stale review, a newer Copilot review was requested July 31, 2026 23:13
Copilot AI previously approved these changes Jul 31, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 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 .md extension and uses the acronym HITL, but the docs-guides corpus uses extensionless relative links (e.g. custom-kernel.md links ./hardware-in-the-loop) and this page otherwise uses HIL. 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 jetm left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/docs-guides/container-dev-mode.md
Copilot AI review requested due to automatic review settings August 3, 2026 15:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 .md extension (the rest use extensionless links, e.g. custom-kernel.md:309 links ./hardware-in-the-loop). Also, this sentence uses HITL while the rest of the page uses HIL, 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.

Copilot AI review requested due to automatic review settings August 3, 2026 15:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 .md extension (./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.

Copilot AI review requested due to automatic review settings August 3, 2026 16:20
jetm and others added 28 commits August 20, 2026 10:42
…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>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 journalctl example looks inconsistent with the rest of the docs in this PR (which refer to the agent as container-agent-dev, e.g. the field note uses container-agent-dev.service). If the actual systemd unit is container-agent-dev.service, this command will fail as written.
journalctl -u avocado-container-agent-dev -f   # pull + restart, as the agent sees it

@nicksinas nicksinas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +395 to +402
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
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole section uses a different naming scheme from the reproducer above it:

  • L396 cites "the config confext in step 2" — step 2 declares one extension, named my-app. There is no config.
  • Unit is app.service here, my-app.service in step 2.
  • Container is avocado-dev here (L408, L409, and this docker ps output), my-app in 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +86 to +115
// 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Colour semantics invert against the guide. docs-guides/container-dev-mode.md:38-45 declares "Blue — on your hostAmber — 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.

Comment thread src/package.json
"file-loader": "^6.2.0",
"jsdom": "^26.1.0",
"mdast-util-from-markdown": "^2.0.2",
"mermaid": "^11.16.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.

Comment on lines +25 to +30
- 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +45 to +55
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
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants