Last development
Task
PBI-122, which puts the pack in the firmware image and the canonical pet on the panel. It completes
E35, so the only open epics of v0.5.0 are E36 and E37.
Changed areas
New frontends/tft/src/sprite.{c,h}, frontends/tft/tests/test_sprite.c,
apps/firmware/include/pet_app_firmware/assets.h, apps/firmware/src/assets.c,
apps/firmware/tests/test_assets.c, and
docs/adr/adr-020-pack-frame-composition-and-whole-scale-placement.md.
Changed components/layout/include/pet_layout/layout.h, src/layout.c, and its test,
components/assets/src/pack_manifest.c, the TFT frontend, render, test, runner, suite header,
CMake, and README, the firmware diagnostics, tests, runner, suite header, CMake, and README,
config/verification.json, platforms/esp_idf/CMakeLists.txt, main/CMakeLists.txt, and
main/app_main.c. Updated the architecture, the PBI, the epic, the limitation register, and the
progress record.
The untracked README.md, DEVELOPMENT.md, and PRODUCT-VARIANTS-DISCUSSION.md at the repository
root belong to the maintainer and were not touched.
What changed
The generated pack is embedded by target_add_binary_data in the main component, because a target
consumes a generated file only in the directory that names it, and the generation target is defined
after the components are registered. The application reaches the bytes through
PetFirmwareAssetSource, a seam shaped like PetFirmwareHeap, and pet_firmware_assets_open
validates them and materialises the manifest into caller-owned storage. app_main holds that storage
statically and emits pack-ready with the version, the entry count, and the length, or
pack-rejected with the acceptance rule the pack broke.
components/layout gained pet_layout_fit_whole, which places a canvas at the largest whole
multiple both extents allow and refuses one the output cannot take even once. The pack path uses it
and the procedural fallback keeps the filling pet_layout_fit, so the 64 by 64 sprites land at 128
by 128 on the panel with every sprite pixel covering the same square.
ADR-020 records that decision, the surround, and the alternatives.
pet_frontend_tft_sprite_draw reads the colour payload and the one-bit mask in place and writes only
the opaque pixels, so the surround shows through wherever a frame is transparent.
pet_frontend_tft_compose_frame clears the surface with the role’s letterbox colour and draws the
frame on it. Every arm of the resolution chain that cannot produce a frame, which is no pack, no
selection, no entry, and a geometry the surface cannot take whole, composes the procedural appearance
instead.
One defect was found on the board and fixed. pet_assets_pack_manifest cleared its destination by
assigning a PET_MANIFEST_EMPTY automatic, which is 13844 bytes against a 3584-byte main task stack,
so the first call overflowed the stack and the board panicked in the tick handler and rebooted in a
loop. It clears in place now, and architecture section 8.8 records the rule.
Tests and checks
./gate.py fast --all-compositions
./gate.py strict --all-compositions
./gate.py audit --all-compositions
python3 tools/verify/embedded.py --require
python3 tools/verify/boundaries.py
every suite under tools/tests
idf.py buildAll passed. pet_layout_tests grew from 10 to 16, pet_frontend_tft_tests from 52 to 63, and
pet_firmware_tests from 51 to 57. The new sprite suite covers the mask bit order, the row stride, a
clipped placement, a scale of one, and every refused geometry and payload. The frontend suite asserts
that the pack’s own bytes reach the frame buffer and that a frame the surface cannot take whole
composes the same bytes as the procedural appearance. The firmware suite covers the pack seam over an
accepted pack, a damaged one, a platform with no pack, and a refusing source.
idf.py build succeeded and the image grew from 0x2d390 to 0x388f0 bytes, which is the 44232-byte
pack plus the sprite and seam code, with the partition 78 per cent free.
Device evidence, on the classic board over /dev/ttyUSB0. The accepted run reported
pet event=pack-ready version=1 entries=5 bytes=44232 and the maintainer observed the canonical pet
on the panel. A pack with one payload byte flipped reported
pet event=pack-rejected reason=check, the run continued with no reset, and the maintainer observed
the procedural fallback, which is a dark surround with a lighter body. The good pack hashes to
a627c8e7368dd3a3470ca42f174a54fb47cdc718721c2cf338ef939d2b9dac9e, the value PBI-118 recorded.
Component files updated
No component knowledge files required updates.
Known limitations
LIM-013 stays open until PBI-128 closes it, which is the release-level review, and its closing
evidence now exists. LIM-012 narrowed to say the panel shows the canonical pet. LIM-020 is
unchanged.
Suggested commits
1. Whole-multiple placement
git add components/layout/include/pet_layout/layout.h components/layout/src/layout.c components/layout/tests/test_pet_layout_fit.cfeat(layout): place a canvas at the largest whole multiple
2. Pack frame composition
git add frontends/tft/src/sprite.c frontends/tft/src/sprite.h frontends/tft/src/render.c frontends/tft/src/render.h frontends/tft/src/frontend.c frontends/tft/tests/test_sprite.c frontends/tft/tests/test_frontend.c frontends/tft/tests/test_runner.c frontends/tft/tests/test_suites.h frontends/tft/CMakeLists.txt frontends/tft/README.mdfeat(tft): compose a pack frame through its colour payload and its mask
3. Manifest materialisation stack cost
git add components/assets/src/pack_manifest.cfix(assets): clear the materialised manifest in place
4. Firmware pack seam and diagnostics
git add apps/firmware/include/pet_app_firmware/assets.h apps/firmware/include/pet_app_firmware/diagnostics.h apps/firmware/src/assets.c apps/firmware/src/diagnostics.c apps/firmware/tests/test_assets.c apps/firmware/tests/test_diagnostics.c apps/firmware/tests/test_runner.c apps/firmware/tests/test_suites.h apps/firmware/CMakeLists.txt apps/firmware/README.mdfeat(firmware): open the embedded pack through a platform seam
5. Embedded pack in the image
git add platforms/esp_idf/CMakeLists.txt platforms/esp_idf/main/CMakeLists.txt platforms/esp_idf/main/app_main.c config/verification.jsonfeat: embed the asset pack in the firmware image
Suggested next step
E36, starting with PBI-123, the pure button filter. It needs no board until PBI-124 adds the
GPIO backend, and PBI-125 is where a filtered edge becomes a semantic action. PBI-126 then runs
the release gates over the whole set, and it now depends on nothing that is still open in E35.