Tool-agnostic brief for coding agents working on this repository.
Driving the robot from an IDE or script is a separate concern — use docs/integration.md and docs/api.md (POST /anim). This file is about changing firmware, packages, docs, CAD, and PCB.
Tiny Engineer is an open-source ESP32-C3 Wi-Fi desk robot: 3D-printed mechanics, REST-controlled animations, and Node packages that map agent hooks to poses.
| Path | Role |
|---|---|
src/, include/, lib/, data/ |
Firmware (PlatformIO) |
ui/ |
Web control panel (Vite, TypeScript, Preact); pio run builds it into one minified page and embeds it gzipped in the firmware |
packages/ |
HTTP / hook CLIs (Cursor, Antigravity, Claude Code, …) — scope integrations |
skills/ |
Agent skills shared by Claude Code, Cursor and Antigravity — scope integrations |
3d_models/ |
CAD and printables (CERN-OHL-S) |
mods/ |
Optional mods; models under mods/<name>/3d_models/ (CERN-OHL-S) |
hardware/ |
KiCad boards (CERN-OHL-S) |
docs/ |
Human docs; depth lives here |
Physical module: Waveshare ESP32-C3-Zero. PlatformIO board = esp32-c3-devkitm-1 is the build target name.
From the repo root (Node 18+ for packages, Node 20.19+ on 20.x or 22.12+ for ui/ and therefore pio run). Host only — CI never flashes.
pio run
pio run -e expression-demo
pio test -e native
node scripts/expressions/generate.js --check
node scripts/expressions/test-assets.js
npm test --prefix packages/tiny-engineer-cli
npm test --prefix packages/tiny-engineer-cursor
npm test --prefix packages/tiny-engineer-antigravity
npm test --prefix packages/tiny-engineer-claude-code
npm run lint --prefix ui
npm test --prefix ui
python3 scripts/check_pcb.pypio test -e native is not pio run -e native. PCB checks need KiCad 10 (python3 scripts/check_pcb.py). Details: docs/testing.md.
Do not flash (pio run -t upload, -t uploadfs, pio run -e ota -t upload, -t ota, or -t otafs) unless the user asks.
Significant: logic, types, APIs, headers, firmware under src/ / include/ / lib/, or platformio.ini.
- Run
pio runand fix failures before considering the work done. - Skip compile for comments-only, docs-only, or tiny formatting.
When adding, removing, or changing method, path, or query params of any HTTP route, update in the same change:
- Web UI API page —
ui/src/views/ApiReference.tsx: endpoint table and param lists for/anim,/settings,/test/servo - API reference —
docs/api.md - Summaries — if the endpoint list, params, or boot URLs changed:
README.mdanddocs/hardware/testing.md - Route table — add the route to
kRoutesinsrc/http/routes.cppwith its access rule. 404 and 405 answers come from that table
Web UI API page: one-line route descriptions only (except supported-parameter lists). Param values must match docs/api.md exactly.
Format: type(scope): summary (imperative, lowercase type, no trailing period; PR title same).
Scopes: firmware, http, settings, anim, servos, wifi, integrations, cad, mods, pcb, docs, scripts, ci.
integrations = anything under packages/ or skills/. Do not add a new scope per package.
mods = anything under mods/. Name the mod in the summary. Do not add a new scope per mod.
Breaking rules, SemVer mapping, and examples: CONTRIBUTING.md.
- Firmware —
pio run; alsopio test -e nativewhen touching settings validation. Pin changes updateinclude/pins.handdocs/hardware/together. - Settings — layer checklist in docs/settings.md. Never log raw
access_token. - HTTP routes — add the route to
kRoutesinsrc/http/routes.cppwith itsAccesslevel;Refusal::checkenforces it. Routes with fields read a JSON body throughreadJsonBody; onlyPOST /anim(name) and/playtake other input, because the agent packages depend on them. - Web UI — Preact + TypeScript in
ui/src, plainpreact/preact/hooksimports (no compat). One component per file; shared components inui/src/components, helpers inui/src/lib.npm run lint --prefix uirunstscand type-checked ESLint, which forbids.then/.catchchains (use try/catch). Keep markup and class names when refactoring: the CSS styles them by class and id. - Integrations — add/extend package tests; prefer short timeouts and ignore network errors so a missing robot does not stall the agent.
- CAD — edit
.f3dand export affected3mf. CERN-OHL-S. Do not swapAiEmblem.3mfas branding. New parts: docs/3d/adding-parts.md. - Mods — optional add-ons under
mods/<mod_name>/. Models live in3d_models/{cad,parts}/. Committype(mods)and name the mod in the summary.feat(mods)/fix(mods)do not version stock CAD. See mods/README.md. - PCB — docs/pcb.md checklist + KiCad review rules below. Run
python3 scripts/check_pcb.py(KiCad 10). New board paths needREUSE.toml. CERN-OHL-S. - Motion — poses −1..1 mapped to saved min/max; see docs/robot-movement.md. Do not widen NVS servo clamps without testing on a real robot.
- Secrets — no
.env, tokens, or Wi-Fi passwords in logs or screenshots.
Truth order (do not skip ahead for connectivity claims):
kicad-clinetlist export- ERC / DRC
- board
expected-nets.yml(compare to netlist) docs/hardware/pinout.md/include/pins.h- board
README.md
Forbidden: infer nets from .kicad_sch / .kicad_pcb X/Y, geometry, or coordinate heuristics.
Required: netlist ↔ expected-nets.yml comparison for connectivity. No kicad-cli → say so and skip connectivity claims (layout/docs review only).
Contribute/review process: docs/pcb.md. expected-nets.yml: docs/pcb.md#expected-netsyml.
- Supply 5 V / ≥ 2 A. Never power servos from the ESP32 3.3 V LDO.
- PCA9685 VCC = 3.3 V logic; V+ = 5 V servo rail. Do not short them.
- Leave the C3-Zero ceramic antenna clear of metal and dense plastic.
| Topic | Doc |
|---|---|
| HTTP API | docs/api.md |
| Drive robot from any IDE | docs/integration.md |
| Settings layers | docs/settings.md |
| Servo axes / ranges | docs/robot-movement.md |
| Tests | docs/testing.md |
| PCB / KiCad | docs/pcb.md |
| Humans contributing | CONTRIBUTING.md |
| Docs index | docs/README.md |