Typical Workflow — Build, Deposit, Review
This is the standard end-to-end flow your AI assistant runs when you ask it to build firmware. Read it once and the rest of the manual makes a lot more sense.
The first 8 steps are the build loop. Steps 9–10 are the sovereignty
loop: every build becomes a signed triple in ~/maker-assets/, and a
browser-driving agent pushes weekly stats to the local /ops/funnel-review
page so you can copy out a Markdown work report.
─── build ────────────────────────────────────────────────────────────
1. Assistant reads install://overview → confirms your setup
2. Assistant runs doctor → checks everything is healthy
3. Assistant runs store_versions → sees IDF v5.3.1 is available
4. Assistant runs project.init (target: esp32s3) → writes .espctl.toml
5. Assistant runs build (target: esp32s3) → returns task_id / build_id
6. Assistant watches build.status until succeeded → tracks progress
7. Assistant runs logs.tail to show build output → shows you what happened
8. Assistant runs artifacts.manifest → shows firmware size + flashable files
─── deposit ──────────────────────────────────────────────────────────
9. Assistant runs `espctl deposit add <build_id>` → signs a triple into ~/maker-assets/
─── review (weekly, browser-driving agent) ───────────────────────────
10. Agent aggregates `espctl deposit list --since/--until --json` →
pushes the snapshot via window.aegis.importMakerStats(...) →
/ops/funnel-review renders metric tiles + work-report Markdown.
Here’s what each step does and why.
1. Read install://overview
Assistant → espctl: read("install://overview")
espctl → Assistant: env-var table, modes, basic setup
espctl ships its own setup guide as a resource. Reading it once at the start of a session gives the assistant an immediate picture of:
- Which env vars you’ve set (and which you haven’t).
- Whether it’s in remote-build mode (the default) or plan-only mode.
- The list of AI tools and their config snippet URLs.
This is also a good first move when troubleshooting — if the resource is unreachable, espctl itself isn’t running, which is the kind of “obvious in hindsight” detail an assistant might miss without checking.
2. Run doctor
Assistant → espctl: doctor
espctl → Assistant: { status: "healthy", checks: [...], errors: [] }
doctor runs a handful of health checks (build server reachable, access key
valid, project settings parse, IDF versions match). If anything is wrong, it
fails fast with a structured error pointing at the offending check.
Run this every time you start a new session, even if it worked yesterday. Catches the most common “wait, why isn’t it working?” failures before you try to do real work.
3. List build server versions
Assistant → espctl: store_versions
espctl → Assistant: { versions: ["v5.2.2", "v5.3.1"], default: "v5.3.1" }
Confirms which IDF version a build will use by default and shows the
alternatives. If your project pins a specific version in .espctl.toml, the
assistant will note any mismatch and either use the pin or fall back to the
default depending on what idf_select_version decides.
4. Initialize the project
Assistant → espctl: project.init { target: "esp32s3" }
espctl → Assistant: { project_root: "...", config_path: ".espctl.toml", ... }
Creates .espctl.toml and the build subfolder. Safe to run twice — if the
project is already set up, this does nothing.
If you’re working on an existing project, skip this step. The assistant will
still run validate_config against the existing .espctl.toml to make sure
nothing’s broken.
5. Start the build
Assistant → espctl: build { target: "esp32s3", profile: "release" }
espctl → Assistant: { task_id: "0abf...e2", status: "pending" }
The build is sent to the build server and starts running in a sandbox. You
get a task_id right away — the build itself runs in the background.
6. Watch until done
loop:
Assistant → espctl: build.status { task_id: "0abf...e2" }
espctl → Assistant: { status: "running", phase: "compiling", progress: 0.42 }
wait 2s
until status == "succeeded" or "failed"
Most assistants check every 1–3 seconds. A more efficient pattern is to
subscribe to the build://log/{task_id} resource and get pushed updates
instead — but checking is simple and works everywhere.
7. Read the logs
Assistant → espctl: logs.tail { task_id: "0abf...e2", lines: 100 }
espctl → Assistant: { lines: [{ seq, ts, stream, text }, ...] }
Once the build finishes, pull the last N lines of output. This is what your assistant shows you as “the build log”.
If the build failed, your assistant will also run parse_build_errors to
extract structured error messages — much more useful than dumping 500 lines
of raw output.
8. Read the manifest
Assistant → espctl: artifacts.manifest { task_id: "0abf...e2" }
espctl → Assistant: { artifacts: [...], flash_size, flash_freq, ... }
The manifest is the official record of what the build produced and how to
flash it. Your assistant can stream individual .bin files to your local
disk for flashing, or hand them straight to the esphome.cloud web flasher.
Security note: Your compiled firmware may contain embedded secrets (Wi-Fi credentials, API keys). Treat
.binfiles as sensitive.
9. Deposit the triple
Assistant → CLI: espctl deposit add 0abf...e2
deposit-flow → ~/maker-assets/: requirement + code + verification triple, signed
Once the build manifest is in hand, espctl deposit add <build_id>
converts that build into a signed (requirement, code, physical verification) triple in ~/maker-assets/. All builds — failed too —
are worth depositing; failure data is part of the asset.
The MCP mirror deposit.add lets an AI agent call this directly after
the build succeeds, prompting the maker only for the three fields it
can’t derive (outcome, evidence, notes). Triples never leave your disk
unless you explicitly run espctl deposit attest to publish to
Zenodo + OpenTimestamps.
See espctl deposit for the
full eight-subcommand surface.
10. Push to /ops/funnel-review for weekly review
Agent → espctl deposit list --since 2026-05-17 --until 2026-05-24 --json
Agent → chrome-mcp evaluate_script:
window.aegis.importMakerStats({ ...aggregated snapshot... })
Agent → opens /ops/funnel-review → reads the rendered Markdown report
The same chrome-mcp / playwright-mcp agent that drove the build can
aggregate this week’s espctl deposit list --json (plus tool-call
counts from agent telemetry) and push the result as a
MakerStatsSnapshot into the browser via window.aegis.importMakerStats.
/ops/funnel-review then renders 6 metric tiles, hardware + tool
charts, 5 health checks, and a copy-paste Markdown work report — all
locally, no upload.
See Weekly Maker Asset Review for the snapshot schema and the browser API contract.
Variations
This is the happy path. Real workflows often diverge:
- Build fails at step 6 → Assistant runs
parse_build_errorsagainst the log, then thediagnose-build-errorprompt. You get a structured “this is what’s wrong, here’s the fix” instead of a wall of text. - You change the chip target → Insert a
set_targetcall between steps 4 and 5. The assistant warns you that this clears the build cache. - You need an interactive serial monitor after the build → Assistant
uses the Monitor tab or
espctl monitor --port /dev/ttyUSB0. - You want to know what the build would do without running it →
Replace step 5 (
build) withgenerate_build_plan. No side effects.
See Browser Wizard for the same flow when you’re clicking through a web page instead of chatting with an assistant, or MCP Console if you want to call the tools by hand.