Replaces the never-executed "LiteRT-LM on UNO Q 4GB" spike plan with a
measured record of the local-fallback path we actually run.
Measured on board 65301572 (Qwen2.5-0.5B-Instruct, -c 8192, 4 threads),
using llama-server's own timings rather than wall clock:
prefill ~17-20 tok/s (linear), decode ~6-11 tok/s (degrades with KV)
warm prefix-cached tool call: 3.8s, 6/6 correct structured calls
Two findings that changed the deployment:
1. The board had drifted onto Qwen2.5-Coder-1.5B - larger and tuned for
the wrong task. Reverting to the repo's 0.5B made tool calls ~6x
faster (24s -> 3.8s) and freed ~700MB. The repo was right.
2. The harness, not the model, was the bottleneck. The default agent
profile sent a 4718-token prompt (~4.6 min prefill) and the client
cancelled before the model could answer. A lean runtime profile cuts
that to 706 tokens, lifts prefix-cache match 0.435 -> 0.966, and
completes a full agentic turn with a real tool call in 11s warm.
The ZeroClaw text parser was never at fault.
Prompt cost model for budgeting profiles: ~706 base (1 tool),
~244/additional tool, +315 for uno_q_flash (schema + flash imperative),
~53/skill in compact mode.
Also standardises context on -c 8192 across all three provisioning paths
(a 16k window costs ~16 min to fill at this speed and doubles KV for
nothing), and fixes stale references to the deleted src/lib/harness.ts.
Adds bench-prefill.sh and bench-tools.py as reproducible baselines.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Vendors the fork's 11 granular UNO Q skills (bridge, flashing, led-matrix,
uno-q-hardware, sketch-patterns, modulino, linux-led, audio, vision, wireless,
arduino-app-lab) next to the comprehensive arduino-uno-q skill, and installs the
whole set into every agent's workspace on each board.
Why both: the comprehensive skill is the rich cloud reference (read_skill →
references); the granular skills are keyword-triggered and match the fork's eager
skill-inliner rules, so the on-board Qwen auto-inlines them (no read_skill
round-trip). flashing + led-matrix carry the exact uno_q_flash + frame-API /
ArduinoGraphics-not-installed detail that makes flashing reliable.
- push-skill.sh generalized: a single skill dir (has SKILL.md) OR a parent dir
installs every skill under it; provision-fleet now ships all of skills/.
- Verified on board 65301572: cloud/Sonnet-5 lists all 12 skills.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Vendors the comprehensive UNO Q skill (SKILL.md + 7 references/*.md) and installs
it into EVERY agent's workspace on each board, so agents know this board's
specifics (dual-brain arch, Bridge/RPC, pin tables, LED matrix + the
ArduinoGraphics-not-installed gotcha) instead of guessing generic Arduino.
Why per-agent workspace: ZeroClaw's read_skill returns only SKILL.md; the agent
reads references/*.md via the workspace-sandboxed file_read tool, so references
are only reachable under ~/.zeroclaw/agents/<alias>/workspace/skills/. A
shared/skills bundle surfaces the skill but its references get sandbox-blocked.
- push-skill.sh installs a SKILL.md+references skill into every agent workspace
(discovers aliases from the board); provision-fleet runs it per board.
- config.template risk profile now allows + auto-approves read_skill + file_read
so agents load skills without a human approver (webhook path is non-interactive).
- Flattened the folded 'description: >-' to single-line (ZeroClaw's frontmatter
parser is a flat scanner, not full YAML).
Verified on board 65301572 with cloud/Sonnet-5: discovered arduino-uno-q →
read_skill(SKILL.md) → file_read references/04-bridge-rpc.md → correct
board-specific answer citing the file.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Boards now boot LAN-open (host 0.0.0.0, allow_public_bind, require_pairing=false)
so a team reaches its node's embedded ZeroClaw dashboard + web chat directly by
IP with no token — the 'Open your node' path. Two more modalities ride the same
default (cloud-first) agent:
- Telegram: config seeds [channels.telegram.default] disabled + pre-bound to the
default agent; a team enables it from the dashboard by pasting a @BotFather
token. Dashboard writes only set pending_reload, and /admin/reload is
loopback-only, so a new zeroclaw-reload-watcher.sh (loopback) applies dashboard
config edits within seconds — no shell. provision-fleet pushes + launches it.
- Lockdown: zeroclaw-lockdown.sh flips require_pairing=true, reloads, and mints a
pair code (open -> locked harden step); documented as a board/adb action since
minting + reload are loopback-only.
- Voice: seeds a commented [channels.voice_duplex.default] + documents the
aarch64 build (embedded-web + gateway-voice-duplex); build/hardware work is
separate.
Onboarding scripts (apess-selfregister.sh, provision-uno-q.sh) now tolerate
open-boot: when the gateway mints no pair code, they announce with a placeholder
token (the open board ignores auth) instead of hard-failing.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Folds the boot animation into the fleet pass — after onboarding each
board (env + self-register + timer/cron), provision-fleet now flashes the
default sketch (sketches/matrix_rain) via flash-sketch.sh. Best-effort: a
flash hiccup logs a warning but doesn't fail the board's onboarding.
- FLASH_DEFAULT=1 (default; 0 skips) and FLASH_SKETCH=<dir> knobs.
- Upfront guards for flash-sketch.sh + the sketch dir.
- provision() refactored to a single success tail (persistence → flash).
Verified on hardware: one pass reports both "ok — onboarding (cron)" and
"ok — boot animation (matrix_rain)".
Co-Authored-By: Claude Opus 4.8 <[email protected]>
- flash-sketch.sh: compile + flash a sketch dir onto a board's MCU over adb
(push → arduino-cli compile with TMPDIR=/tmp → arduino-flash @ 0x80F0000).
Defaults to sketches/matrix_rain; includes a fleet loop over adb devices.
DRYs up the recipe and makes "set the board default" one command.
- Designate matrix_rain as the boot animation boards ship with (the flashed
MCU sketch persists across power cycles). Documented in the sketches README
+ parent Files list.
Flashed matrix_rain to the board via the helper (821 bytes, verified E2E).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Two host-side helpers that close the operational gap between minting kit
codes and boards-in-hand.
- provision-fleet.sh: provision a whole fleet from fleet.csv (kit,serial).
Per board it pushes the per-kit apess-node.env + apess-selfregister.sh
over adb and enables the boot/refresh systemd timer (MODE=systemd, root)
or an equivalent cron (@reboot + every 5 min; the no-root default).
fleet.csv.example is the assignment template.
- gen-qr-sheet.sh: render the sticker CSV into a self-contained, printable
QR sheet (A4, ~9/page). QRs are baked in as inline SVG via qrencode, so
the HTML has zero external refs — prints offline. Each sticker: kit id,
QR of the claim URL (/workshop?kit=..&code=..), and the 6-digit code.
- .gitignore: never commit kit-codes/ (env files hold FLEET_SECRET +
claim codes) or fleet.csv.
- README: documents both, plus the one-command fleet path.
Verified: provision-fleet loop logic (header/comment/missing skips) and a
4-kit end-to-end sheet build (4 inline SVGs, 0 external refs).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Board-side half of "preloaded + self-register + claim", plus the QR flow.
- apess-selfregister.sh (on-board): pairs locally for a token, discovers
the LAN IP, and announces {kitId, url, token, claimCode} to APESS
/nodes/self-register (x-fleet-secret gated). Retries until APESS is up;
idempotent, safe on boot and on a timer.
- systemd/apess-selfregister.{service,timer}: self-register After the
daemon, re-announce every 5 min so a DHCP lease change can't strand a
board.
- apess-node.env.example: per-board identity (KIT_ID, CLAIM_CODE,
FLEET_SECRET, APESS_URL).
- gen-kit-codes.sh (host): mint per-kit 6-digit codes, write the
per-board env files, and emit the QR sticker CSV
(…/workshop?kit=KIT-NN&code=NNNNNN).
- web: BoardClaim accepts initialCode; TeamRegistration pre-fills it from
?code= so scanning the kit QR fills kit + code — one tap to claim.
- deploy/uno-q/README: documents the whole self-serve onboarding path.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
A sudden USB/adb drop breaks the Uno Q node in ways that don't self-heal:
adb tunnels vanish, held-shell services die, llama wedges (alive but not
serving), and flashes silently stop landing. systemd is the clean fix with
root — but the dev board's account is expired (no sudo) and has no user
session bus, so neither system nor user units run. These are the no-root
equivalents, both validated on hardware:
- zeroclaw-supervisor.sh — on-board watchdog. Polls the /health ENDPOINTS
(a wedged process passes pgrep but fails here) and restarts llama / the
daemon on death or wedge. Launches children with `setsid … exec` so they
survive the launching shell — the property `nohup … &` in adb shell lacks.
Startup grace avoids reaping llama mid-cold-load; single-instance lock;
no per-restart shell leak. Proven: kill -9 the daemon → auto-restarted.
Boot-persisted via `@reboot` crontab (no root).
- recover-uno-q.sh — host-side. After the board is back, re-does adb + both
tunnels (out :8080, back :8090 shim), ensures the supervisor is running,
and health-checks every hop by endpoint. Proven end-to-end after a
simulated tunnel drop.
README: new Resilience section documenting the no-root reality + both tools.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Codify the verified board setup into a repeatable kit under deploy/uno-q/:
- config.template.toml — the tested node config: cloud / cloud+fallback /
on-board-Qwen providers, three agents (default / cloud / local) on the
hardware risk profile, gateway, peripherals. Secrets stripped; cloud
endpoint substituted at provision time.
- provision-uno-q.sh — one-shot adb-driven provisioner (no root): install
config, start llama-server + `zeroclaw daemon`, pair for a bearer token,
and POST /nodes to APESS.
- systemd/{zeroclaw-llama,zeroclaw-daemon}.service — production units
(daemon, not `gateway start`, so peripheral tools register).
- README.md — dev (adb) and production (systemd + LAN bind) runbooks.
The individual steps are hardware-verified: all three agent aliases resolve
and route correctly on the board (local→Qwen, cloud→cloud, default→cloud
with Qwen fallback). Full script orchestration + live phone-home land once
the api (with /nodes) is redeployed.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>