Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
889bc4e
HIL: add an ST-LINK flasher and commission stm32h563_nucleo (#550)
hathach Oct 15, 2026
d1d9c66
CLAUDE.md: keep a dependency worktree outside the checkout (#556)
hathach Oct 15, 2026
48b8e8c
espressif: vendor led_strip 3.1.0~1 for ESP-IDF 6 (#525)
hathach Oct 16, 2026
4d59f25
espressif_esp32s3_devkitc_1: note the v1.0 NeoPixel pin, board ID v1.…
hathach Oct 16, 2026
f6b81d9
Add Metro ESP32-P4 support with ADC double-reset detection
ladyada-eagleclaw Oct 14, 2026
b0f2d0d
Fix Metro P4 USB setup in dfu_runtime and tidy the P4 integration
hathach Oct 15, 2026
2ad65aa
Merge pull request #487 from adafruit/metro-esp32p4-adc-double-reset
hathach Oct 16, 2026
dde8ea6
hil: rename the HIL contract to hil-contract and keep only TinyUF2's …
hathach Oct 17, 2026
006f416
hil: take board locks from the pinned agentrc submodule (#569)
hathach Oct 18, 2026
7bb08e7
hil: run the battery on the ci.lan rig from CI (#576)
hathach Oct 18, 2026
b24c4de
hil: record the Port Authority's per-port VBUS switching; document th…
hathach Oct 18, 2026
678c225
HIL: bound and track Ninja queries (#572)
Firat17aze Oct 19, 2026
267d80e
Add SAMD51 port with the Intech Studio Grid bootloader boards
SukuWc Oct 16, 2026
3c69663
samd51: add NEOPIXEL_POWER_STATE like the espressif boards
SukuWc Oct 19, 2026
41569ae
samd51: follow the UF2 Board-ID format with a revision token
SukuWc Oct 19, 2026
ce6db76
HIL: install through agentrc's flash provider, with the P4's JTAG rou…
hathach Oct 20, 2026
18b1fc1
Clarify HIL state after probe-connect crashes (#571)
Firat17aze Oct 20, 2026
261cde5
Document shared-build ELF paths (#573)
Firat17aze Oct 20, 2026
02016e2
Report failing build contract test details (#558)
Firat17aze Oct 20, 2026
0c481d7
Refuse dependency updates through symlinks (#577)
Firat17aze Oct 20, 2026
61d539d
samd51: count NeoPixel timing in cycles
SukuWc Oct 20, 2026
cc7d478
Merge pull request #9 from adafruit/master
SukuWc Oct 20, 2026
6e8d0c9
samd51: derive the application flash size from the board
SukuWc Oct 20, 2026
7f3a0a5
samd51: reject a bootloader image with an even reset vector
SukuWc Oct 20, 2026
c37ebbf
Remove the Intech Studio ESP32-S3 boards from this PR
SukuWc Oct 20, 2026
86bb3d6
Merge remote-tracking branch 'origin/samd51-intech-grid' into samd51-…
SukuWc Oct 20, 2026
e5d523f
Remove the espressif detection delay change from this PR
SukuWc Oct 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .claude/skills/build/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ python3 $B ... --idf local # exported ESP-IDF instead of CI

The last stdout line is JSON. An agent acts on `pass`; `firstError`, and `error` on exit 2, naming the cause or the remedy; `boards[]` (`status` `ok`, `failed` or `error`, `log`, `artifacts`, `notImplemented`); `built`, the artifacts of `--shared` roots, the only ones kept after a pass, with each one's hash in `boards[].sha256`; `nothingToBuild` and `uncovered`, each path with its reason; and `idf.matchesCi`. `input` and `defines` record what a build used (HEAD, `dirty`, submodule and `get_deps` dependency commits, `dirtyInputs` for any of those with local changes, `inputsDirtiedByBuild` for any that changed or moved while building, `.` for the checkout itself): the HIL contract binds firmware to this record. `paths` gives every changed path's `rule` and `reason`, the answer to why a path selected what it did. Exit 0 pass, 1 a build, self-test or test failed, 2 usage, resolution or a missing prerequisite, 3 an uncovered path; 2 outranks 1, and 1 outranks 3. `-v` streams the build to stderr; each build's log is `<buildDir>/check_build.log`.

`--select-only` prints the selection (`build.{ports,builds,ghostfat,tests,paths,ci}`) and stops before any toolchain check: what any agent or CI step reads for "what does this change affect". With `--hil-config <the host's roster>` it adds `hil.{boards,full,uncovered}`: the roster boards the change puts under the HIL battery, and as uncovered each selected port with no roster board on this host and each app source (`apps/`, `ports/<port>/apps/`) but `dfu_runtime`'s, which the battery never runs. `dfu_runtime` selects the esptool roster boards of the ports that build it, and is uncovered once for each port that builds it without one.
`--shared` also keeps the bootloader ELF for debugging. Make places it at `cmake-build/cmake-build-<board>/ports/<port>/tinyuf2-<board>.elf`; CMake and ESP-IDF place it at `cmake-build/cmake-build-<board>/tinyuf2.elf`. With `--backend cmake` on a make port, the root is `cmake-build/cmake-build-<board>-cmake` and the ELF its `tinyuf2.elf`.

`--select-only` prints the selection (`build.{ports,builds,ghostfat,tests,paths,ci}`) and stops before any toolchain check: what any agent or CI step reads for "what does this change affect". With `--hil-config <the host's roster>` it adds `hil.{boards,full,uncovered}`: the roster boards the change puts under the HIL battery, and as uncovered each selected port with no roster board on this host and each app source (`apps/`, `ports/<port>/apps/`) but `dfu_runtime`'s, which the battery never runs. `dfu_runtime` selects the Espressif roster boards of the ports that build it, and is uncovered once for each port that builds it without one.

## Reporting

Expand All @@ -40,10 +42,10 @@ An agent that reports `buildOk` and `notes` (agentrc's `code-writer`) maps the J
- `build.ci` is CI's selection from the same rules (`.github/workflows/ci_set_matrix.py` reads it on a pull request): every board a change reaches, not the representatives. A board's own files select that board; a port file, dependency, shared app or `make.mk`/`rules.mk` every board of the ports it reaches; a port app the boards that enable it. Shared code and a change to how boards build select `full`, every board and the ghostfat self-test; a ghostfat input the self-test alone. Tests, docs, CodeQL's build and uncovered paths select nothing.
- **Builds are CI's.** Each port uses the backend `build.yml` gives it (`BACKENDS` in the script; `test_build.py` fails when the two disagree): make `all self-update`, then `copy-artifact`, then every `ports/<port>/apps/*` but `self_update`; cmake `-G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel`, which also builds each app the port's `CMakeLists.txt` adds with `family_add_app()` as its own project; Espressif `idf.py build`. A port moves from make to cmake in `build.yml` once a board of it passes the HIL battery with cmake. A board passes only with the artifacts its build must produce (`expected_artifacts()`): the bootloader images and the self-update and app images the port builds (cmake: its `family_add_app()` calls enabled for the board). A port without self-update, or a stub app, is listed in `notImplemented`, which is not a failure.
- **`--backend cmake`** is for the HIL's receipt only, with `--board` and `--shared`: on a make port it is no CI evidence, and it is how such a port's board gets the HIL run that moves the port to cmake. Espressif is refused. `-D` takes the cmake spellings: `LOGGER=RTT`, not `rtt`; `DEBUG` is ignored, the build stays MinSizeRel; `LOG` (default 0, as in make) sets `TUF2_LOG` and `CFG_TUSB_DEBUG`; raw flags go through `CFLAGS_CLI`.
- **A HIL record** (`--board --shared --hil-config <roster>`) binds the build's own flash target to the board's roster flasher: a J-Link board is configured with `JLINK_OPTION=-USB <probe serial> -ExitOnError 1 -NoGui 1` (so `tinyuf2-jlink` selects that probe and fails non-zero), an esptool board's ESP-IDF `flash` target takes the bridge's tty at flash time. `boards[].flash` holds the target, the roster flasher, the target's generated command (checked to name that probe or chip and the build's own image), the configure defines that bound it and the build-tree files it reads (in `sha256` too); a rebuild and a HIL flash hold the build dir's lock (`<build dir>.lock`). Another flasher, a roster `args` other than `-device <name>` or `--chip <chip>`, or a `-D` of `JLINK_OPTION`, `OPENOCD_SERIAL`, `OPENOCD_OPTION` or `ESPPORT` is refused. Before flashing, the HIL dry-runs that target under the lock and refuses any command but the flasher and, on ESP-IDF, the generated ones that write no flash input (its bootloader subproject dry-run too); ESP-IDF re-runs CMake after any commit (its `git describe` dependency), so rebuild the record on the commit under test.
- **A HIL record** (`--board --shared --hil-config <roster>`) binds the build's own flash target to the board's roster flasher: a J-Link board is configured with `JLINK_OPTION=-USB <probe serial> -ExitOnError 1 -NoGui 1` (so `tinyuf2-jlink` selects that probe and fails non-zero), an ST-LINK board with `STLINK_SERIAL=<probe serial>` (so `tinyuf2-stlink` connects to that probe and writes the HEX), an esptool board's ESP-IDF `flash` target takes the bridge's tty at flash time. The HIL never runs these targets: it passes the bound files to agentrc's flash provider, so the binding is a build-time check that the target and the roster agree. `boards[].flash` holds the target, the roster flasher, the target's generated command (checked to name that probe or chip and the build's own image), the configure defines that bound it and the build-tree files it reads (in `sha256` too); a rebuild and a HIL flash hold the build dir's lock (`<build dir>.lock`). `boards[].flashRoutes` binds each of the board's `tinyuf2.routes` (`openocd_esp`, `-f board/<chip>-builtin.cfg`, the USJ serial as `uid`) to the files the esptool binding hashed, for the same chip. Another flasher, a roster `args` other than `-device <name>` or `--chip <chip>`, or a `-D` of `JLINK_OPTION`, `STLINK_SERIAL`, `OPENOCD_SERIAL`, `OPENOCD_OPTION` or `ESPPORT` is refused. ESP-IDF's records go stale at the next commit (its `git describe` dependency re-runs CMake), so rebuild the record on the commit under test.
- **ESP-IDF** builds in CI's image, `espressif/idf:<build.yml's toolchain_version>`, through docker by default; a missing image is exit 2 naming the `docker pull`. `--idf local` uses the exported IDF and records its version and `matchesCi`: evidence from a different IDF is supplemental, not acceptance.
- **ghostfat** builds each `ports/test_ghostfat/boards/*` config natively with cmake and runs its `selftest` target, which compares the generated image with the config's known-good one, in the private dir, one config at a time: `huge` needs about 3 GiB of disk while it runs. A passing config's dir is removed; a failing one keeps its log.
- **Private dirs for parallel agents.** Each build starts from an empty `cmake-build/cmake-build-agent-<pid>-<board>` (with `-<backend>` when a board builds with another backend than its port's) and is removed when it passes (`removed`; its `artifacts` say what it produced, nothing is left to flash); a failed one is kept for its log. Nothing is written into the source tree: make takes `BUILD_ROOT=<dir>`, cmake and idf `-DARTIFACT_PATH=<dir>/_bin`. `--shared` rebuilds `cmake-build/cmake-build-<board>` from clean and keeps it for flashing; it needs exclusive ownership of that board.
- **Dependencies are never fetched here.** `lib/tinyusb` and `lib/uf2` are submodules (`git submodule update --init lib/tinyusb lib/uf2`); a port's `tools/get_deps.py` rows must exist. In a worktree, symlink them from the primary checkout (CLAUDE.md), never run `get_deps.py` through a symlink: it runs `git reset --hard` in each dependency.
- **Debug builds** for a probe session use the port's backend directly: make `make -C ports/<port> BOARD=<b> DEBUG=1 LOG=2 LOGGER=rtt all` (`LOGGER=swo` too); cmake `-DCMAKE_BUILD_TYPE=Debug -DLOG=2 -DLOGGER=RTT` (only `RTT` adds the RTT sources). Flash targets: make `flash-jlink`, `flash-pyocd`, `flash-stlink`, `flash-uf2`; cmake `tinyuf2-jlink`, `-stlink`, `-openocd`, `-pyocd` (lpc55, kinetis_k32l2), `-dfu-util` (stm32l4), and `<app>-uf2` for each app; Espressif `idf.py -C ports/espressif -DBOARD=<b> flash`.
- **Dependencies are never fetched here.** `lib/tinyusb`, `lib/uf2` and `lib/agentrc` (the HIL lock helper the contract tests load) are submodules (`git submodule update --init lib/tinyusb lib/uf2 lib/agentrc`); a port's `tools/get_deps.py` rows must exist. In a worktree, symlink them from the primary checkout (CLAUDE.md); `get_deps.py` refuses to update a symlinked dependency path.
- **Debug builds** for a probe session use the port's backend directly: make `make -C ports/<port> BOARD=<b> DEBUG=1 LOG=2 LOGGER=rtt all` (`LOGGER=swo` too); cmake `-DCMAKE_BUILD_TYPE=Debug -DLOG=2 -DLOGGER=RTT` (only `RTT` adds the RTT sources). Flash targets: make `flash-jlink`, `flash-pyocd`, `flash-stlink`, `flash-uf2`; cmake `tinyuf2-jlink`, `-stlink`, `-openocd`, `-pyocd` (lpc55, kinetis_k32l2), `-dfu-util` (stm32l4), and `<app>-uf2` for each app; Espressif `idf.py -C ports/espressif -DBOARD=<b> flash`. On ports built with `-flto`, a `static` function may be inlined and absent as a standalone symbol; debug its caller, or use a local instrumented build that marks the function `__attribute__((noinline))`.
- **Printing a command is not verification.** A verdict is the JSON of a run that happened; an agent that cannot run the script reports the build as not run.
Loading
Loading