Release v0.4.0 evidence
Release: v0.4.0
Status: Completed
Source reference: unreleased at the time of writing. No annotated tag or frozen branch exists
yet. Tagging v0.4.0 and creating release-v0.4.0 remain maintainer steps under
section 7 of the release process.
Verification date: 2026-08-04
Required gates
| Gate | Command | Result | Notes |
|---|---|---|---|
| Fast | ./gate.py fast --all-compositions | Passed | Contained in the audit gate below |
| Strict | ./pet.py verify all | Passed | The required completion gate for every implementation PBI, run per composition as each one closed |
| Audit | ./gate.py audit --all-compositions | Passed | Final release gate, run from a clean tree over headless and desktop-sdl |
The audit gate begins with its own clean step, so the release result was produced from an empty build
tree rather than from an incrementally reused one. It was run once to collect the evidence below and
again after the project version was set to 0.4.0 and the magic rejection test was widened, so the
recorded result belongs to the tree that will be tagged.
The first run reported 181 of 184 and 182 of 184 Core branches rather than every one, because the magic rejection test damaged only the last of the four magic bytes and the earlier comparisons never took their failing branch. The test now damages each position in turn, and the recorded run covers every branch.
Release checks
Every row was produced by ./gate.py audit --all-compositions unless noted.
| Check | Evidence | Result | Notes |
|---|---|---|---|
| GCC build/test | Included in audit gate | Passed | 12 CTest cases desktop-sdl, 11 headless |
| Clang build/test | Included in audit gate | Passed | Same suites, second toolchain |
| ASan | Included in strict or audit gate | Passed | Both compositions |
| UBSan | Included in strict or audit gate | Passed | Both compositions |
| Cppcheck | Included in fast, strict, or audit gate | Passed | 85 sources analysed |
| Clang-Tidy | Included in strict or audit gate | Passed | Project code only, both compositions |
| Formatting | Included in fast, strict, or audit gate | Passed | 127 files match the style |
| Public header self-containment | Included in strict or audit gate | Passed | 25 headers desktop-sdl and 23 headless, GCC and Clang |
| Core boundary symbols | Included in strict or audit gate | Passed | GCC needs nothing external, Clang needs memcpy and memset in 3 objects each, and no Core compile line names a file or allocation dependency |
| Include boundary | Included in fast, strict, or audit gate | Passed | boundaries.py, 12 boundaries hold over 268 scanned source files |
| Embedded boundary | Included in fast, strict, or audit gate | Passed | embedded.py, 12 project sources hold the boundary against the ESP-IDF compile database |
| Component paths | Included in fast, strict, or audit gate | Passed | 3 declared components are present |
| Asset manifest and runtime layout | Included in fast, strict, or audit gate | Passed | assets.py, 1 asset set with 5 manifest references resolving under each build output |
| Runtime allocation probe | Included in audit gate | Passed | 0 allocations and 0 memory errors in both compositions |
| Allocation probe coverage | Included in strict or audit gate | Passed | probe.py, 11 public Core operations exercised, in every composition and toolchain pass |
| Coverage | Included in audit gate | Passed | Measures src only |
| Valgrind | Included in audit gate | Passed | The allocation probe reports 0 allocations and the test suite 16 framework allocations, with 0 errors per composition |
| Composition selection | python3 tools/tests/test_compositions.py, contained in the gate | Passed | 31 composition tests |
| Gate selection | python3 tools/tests/test_gate.py, contained in the gate | Passed | 36 gate tests |
| Project command entrypoint | python3 tools/tests/test_pet.py, contained in the gate | Passed | 32 project command tests |
| Dependency overrides | python3 tools/tests/test_dependencies.py, contained in the gate | Passed | 7 dependency override tests |
| Verification policy | python3 tools/tests/test_verification.py, contained in the gate | Passed | 105 verification tests |
| Process-level round trip | pet_headless_round_trip, contained in the headless gate | Passed | Recorded below |
| ESP-IDF build proof | idf.py set-target esp32 and idf.py build | Passed | Recorded below, run outside the hosted gate |
| Support matrix | python3 tools/verify/matrix.py | Passed | Release-time step rather than a gate step |
The five tool suites above run as gate steps and total 211 tests.
Persistence evidence
The Core suite runs 209 tests, of which the save and load suites own 56.
| Area | Suite | Tests | Notes |
|---|---|---|---|
| Save | pet_save | 20 | Encoding, capacity, null arguments, and the reported length |
| Load | pet_load | 36 | Round trip, absence, and rejection, of which 20 are rejection tests |
| Storage contract | pet_storage_tests | 23 | Dispatch validation, the stdio implementation, and the contract over both implementations |
| Shared app sequence | pet_app_tests | 10 | The stage a result came from, over the memory double |
| Headless app | pet_headless_tests | 24 | Options, diagnostics, and a whole run over the memory double |
| Desktop app | pet_desktop_tests | 43 | Includes a 7 test persistence suite, the wall clock, and the storage diagnostic category |
The rejection tests cover wrong magic in each of its four byte positions, an unknown format version, a wrong length, a failed integrity check, out-of-domain activity, expression, name length, and name padding, dwell stamps and an update count beyond the elapsed time, and a candidate that would overflow the world clock. Every one asserts that the destination world is byte for byte untouched.
pet_headless_round_trip runs the headless application to save a world into a real file and again to
load it, and asserts that the restored initial snapshot equals the saved final snapshot. A second
pair of runs supplies both wall-clock values and asserts an absence of exactly one day reaching the
world clock. It then refuses a lengthened payload, a payload of foreign bytes, and a removed payload,
so a run that ignored what it was asked to load could not pass. The payload lives under the build
directory and is removed, so the check leaves no file in the source tree.
Embedded build evidence
Run from platforms/esp_idf after . "$HOME/.local/opt/espressif/export.sh".
| Item | Value |
|---|---|
| Commands | idf.py set-target esp32 then idf.py build |
| Framework | ESP-IDF v6.0.2 |
| Toolchain | bundled Xtensa GCC 15.2.0, xtensa-esp32-elf-gcc |
| Target | esp32, derived from the classic T-Display variant |
| Result | configured, compiled, and linked pet_firmware.elf |
| Image | pet_firmware.bin at 0x1e120 bytes, 88% of the smallest app partition free |
| Storage | components/storage/src/storage.c compiled into libpet_storage.a, defining pet_storage_read and pet_storage_write and no implementation |
| Boundary | python3 tools/verify/embedded.py --require holds over 12 project sources against that build |
| Device | no board was flashed or run |
The storage contract is compiled for the target with no implementation selected, which is what
LIM-015 records. The firmware application links no storage dependency and keeps no pet across a
power cycle, so this is build evidence for the boundary rather than any embedded persistence claim.
Coverage
| Target | Line coverage | Branch coverage | Notes |
|---|---|---|---|
| Pet Core | 100.0% | 100.0% | 299/299 lines, 184/184 branches, measured over src in both compositions, from _reports/coverage |
The desktop app, the headless app, the SDL frontend, the firmware app, and the shared components are
exercised by their own CTest suites rather than by the Core coverage figure, which measures src
alone.
Desktop runtime evidence
Taken from build/linux-gcc-debug-desktop-sdl as the working directory.
| Command | Result | Kind |
|---|---|---|
./apps/desktop/pet_desktop --steps 240 --step-ms 16 --save <path> | Exit 0, the reported line unchanged, and an 84-byte payload written at the given path | Automated process |
./apps/desktop/pet_desktop --steps 240 --step-ms 16 --load <path> | Exit 0, same reported line, on the real Wayland driver | Automated process |
SDL_VIDEODRIVER=x11 ./apps/desktop/pet_desktop --steps 240 --step-ms 16 --load <path> | Exit 0, same reported line, on the real X11 driver | Automated process |
./apps/desktop/pet_desktop --load <absent path> | Exit 1, component=storage operation=storage_read status=PET_STORAGE_MISSING code=1301 | Automated process |
./apps/desktop/pet_desktop --load <damaged payload> | Exit 2, component=core operation=world_load status=PET_ERR_INVALID_ARGUMENT code=2501 | Automated process |
pet_desktop observed by the maintainer on Wayland and X11 | Carried forward from v0.3.0, where the window with the pet drawn in it, a visible greet response to the space key and the left mouse button, a greet settling from the happy expression into the attentive activity, and a clean exit through the window close button and Ctrl-C were all observed | Manual observation |
The five automated rows are the desktop file evidence for the release. They prove that the operator’s path is the only place a payload comes from, that a payload survives the process, and that a missing file and a damaged one separate into the app and Core exit statuses.
Visible rendering and synthesised interaction remain outside automated verification, which LIM-011
records, so the manual row is the only evidence for either. It is carried forward from v0.3.0
rather than repeated by hand, which LIM-011 allows, because nothing in this release touches
rendering, the frontend, or input. The five automated rows above prove that the persistence work
leaves the lifecycle intact on both real drivers, and the carried row covers what is drawn and what
an input device produces.
Support status
| Environment | Status | Notes |
|---|---|---|
| Linux GCC | Verified | Built and tested, GCC 16.1.1, plus the real-driver desktop save and load runs |
| Linux Clang | Verified | Built and tested, Clang 22.1.8 |
| Windows MSVC | Designed | Presets provided but never executed. The symbol scan and the allocation check cannot run there at all, see LIM-001 and LIM-002. No Windows claim changed in this release, and the storage replacement fallback is unverified there |
Embedded, classic T-Display for esp32 | Cross-compile verified | Configured, compiled, and linked through ESP-IDF v6.0.2, with the storage contract compiled and no implementation selected. No hardware runtime claim and no persistence claim, see LIM-012, LIM-013, and LIM-015 |
| Embedded, any other board or target | Designed | Not implemented and not built |
Persistence is claimed for the hosted applications only, and only when an operator supplies a path.
No automatic save, no automatic load, no save location policy, and no embedded storage is claimed,
which LIM-008, LIM-015, and LIM-017 record.
Verified toolchain
| Tool | Version |
|---|---|
| CMake | 4.4.0 |
| Ninja | 1.13.2 |
| GCC | 16.1.1 |
| Clang and clang-format | 22.1.8 |
| Cppcheck | 2.21.0 |
| Gcovr | 6.0 |
| Valgrind | 3.27.1 |
| GNU nm | 2.45.0 |
| Python | 3.13.14 |
| ESP-IDF | 6.0.2 |
| Xtensa GCC | 15.2.0 |
The verification host was Linux x86_64. tools/verify/matrix.py prints the hosted set from the
environment it runs in. The ESP-IDF and Xtensa rows come from the embedded build above. The host GCC
moved from 15.3.0 to 16.1.1 between v0.3.0 and this release.
Notes
- The project version in
cmake/pet_version.cmakeis0.4.0, andpet_version_stringreturns it through the generatedpet/version.h. - No check required for the release failed. The unavailable Windows checks and the absent hardware
runtime evidence are recorded as not run rather than passing, and neither is required for this
release. The manual desktop observation is carried forward from
v0.3.0underLIM-011rather than repeated. - The full claim-to-evidence mapping is in section 10.5 of the version architecture.
- This evidence is valid for the commit that will be tagged
v0.4.0. If any source changes before tagging, the final required gate must run again against the exact commit to tag. - Future fixes require a new commit and a patch release tag, such as
v0.4.1.