Project progress
| Document version | Date | Summary |
|---|---|---|
| v1 | 2026-07-18 | Initial progress record |
| v2 | 2026-07-20 | Record completion of E08 architecture planning |
This append-only table records completed project tasks. Add one concise row after a task satisfies its acceptance criteria. Do not record incomplete work as progress.
Completed v0.1.0 progress is archived in release v0.1.0.
The table below records v0.2.0 onwards.
| Date | PBI or task | Outcome | Verification | Notes |
|---|---|---|---|---|
| 2026-07-20 | E08, PBI-035 through PBI-038 | Completed the v0.2.0 architecture and compatibility contracts | Markdown diff checks passed. No code gate run because the task changed documents only | Added architecture, SDL ADR, asset ADR, frontend contract, and completion markers |
| 2026-07-20 | PBI-039, PBI-040 | Added the schema version 1 composition registry and its standard library Python tool | python3 tools/tests/test_compositions.py passed with 26 tests. ./gate.py fast and ./gate.py strict passed | Registry keeps compatibility app-owned. Gate and CMake selection stay with PBI-041 and PBI-042 |
| 2026-07-20 | E09, PBI-041, PBI-042 | Completed composition selection in the gate and in direct CMake, closing E09 | ./gate.py strict passed over headless. ./gate.py fast --app desktop and --all-compositions passed. 50 tool tests passed. Direct CMake valid and invalid pairs verified | CMake reads the registry directly, so compatibility has one source. PET_BUILD_HEADLESS became PET_BUILD_APP |
| 2026-07-20 | E10, PBI-043, PBI-044 | Added the pinned SDL dependency and buildable frontends/sdl target skeleton, closing E10 | ./gate.py audit --app desktop passed from a removed build/ tree, with 9 public headers checked and no external Core symbol. ./gate.py fast --all-compositions, 26 composition tests, 25 gate tests, and direct CMake valid/invalid composition checks passed | SDL is pinned to 3.4.10 through frontend-only FetchContent with system includes. Optional SDL X11 extensions are pinned off, recorded as LIM-006. Local overrides remain tracked by LIM-005; LIM-004 now only covers missing apps/desktop |
| 2026-07-20 | PBI-045 | Added the pet_desktop app skeleton and the shared process diagnostic and exit status contract | ./gate.py strict --app desktop passed with 11 public headers checked. ./gate.py fast --all-compositions, 31 composition tests, 26 gate tests, pet_desktop_tests, and pet_desktop_scenario passed | Exit categories and diagnostic ranges live in apps/include/pet_app/status.h as a contract, not a host library. LIM-004 is closed and held closed by a check-paths gate step; LIM-005 and LIM-006 stay open |
| 2026-07-20 | E11, PBI-046, PBI-047 | Implemented the desktop world and frontend lifecycle and the bounded elapsed time policy, closing E11 | ./gate.py strict --app desktop passed with 13 public headers checked. ./gate.py fast --all-compositions, 31 composition tests, 26 gate tests, and the 4 CTest cases of the desktop composition passed | Component tests moved to cmocka by ordering tests/ before the component directories, so the one pinned copy is reused. LIM-005 and LIM-006 stay open |
| 2026-07-21 | PBI-048 | Defined the manifest schema and development runtime asset layout for canonical pet presentation assets | Documentation-only change. python3 tools/tests/test_compositions.py, python3 tools/tests/test_gate.py, and ./gate.py fast passed. No compiler gate needed because no code, build, or tooling files changed | Common grammar starts manifest <kind> <version>, pet manifest is manifest pet 1. Git-tracked assets/README.md and assets/pet/README.md plus git-ignored docs/asset-manifest-v1.md. sprites/ is the pet image directory. E12 stays open with PBI-049 through PBI-051 |
| 2026-07-21 | PBI-049 | Implemented and tested the bounded manifest parser as the shared components/manifest component | ./gate.py strict passed over headless and ./gate.py strict --app desktop passed with 14 public headers checked. ./gate.py fast --all-compositions, 31 composition tests, 26 gate tests, and the 54 pet_manifest_tests cases passed | The parser is shared rather than SDL private, because the grammar is common to future manifest kinds. components/ is a new shared component layer that every composition builds. The role limit of 64 and the line terminator and byte rules were added to the manifest contract. E12 stays open with PBI-050 and PBI-051 |
| 2026-07-21 | PBI-050 | Added the canonical pet asset set, its runtime copy target, and the shared components/assets loading and fallback layer | ./gate.py strict passed over headless and ./gate.py strict --app desktop passed with 16 public headers checked. ./gate.py fast --all-compositions, 31 composition tests, 26 gate tests, the 20 pet_assets_tests cases, and the 2 pet_assets_runtime_tests cases passed | The asset layer reads through an injected reader and checks the PNG signature of every referenced image, so its tests never touch the filesystem. An asset set is accepted whole or not at all, and the built-in fallback is one calm colour and frame duration. SDL image decoding stays with PBI-051 and drawing with PBI-054, so E12 stays open |
| 2026-07-21 | E12, PBI-051 | Implemented SDL image loading from accepted asset records, closing E12 | ./gate.py strict --app desktop passed with 7 CTest cases and 16 public headers checked, including ASan with detect_leaks=1 over the partial load cleanup. ./gate.py strict passed over headless, ./gate.py fast --all-compositions, 31 composition tests, 26 gate tests, and the 24 pet_frontend_sdl_tests cases passed | Decoding is isolated in frontends/sdl/src/assets_sdl.c through SDL_IOFromFile and SDL_LoadPNG_IO, so a later shared components/png replaces an implementation rather than a contract. Image handles stay opaque in the public header. pet_desktop loads once after create and reports assets= and images= |
| 2026-07-22 | PBI-052 | Added the shared components/presentation component and mapped snapshots to presentation roles through it | ./gate.py strict passed over headless and ./gate.py strict --app desktop passed with 8 CTest cases and 17 public headers checked. ./gate.py fast --all-compositions, 31 composition tests, 26 gate tests, the 7 pet_presentation_tests cases, and the 26 pet_frontend_sdl_tests cases passed | Role priority and the canonical role identifiers live in one component that needs no SDL, filesystem, clock, or allocation, so a second frontend links it rather than copying the policy. PET_PRESENTATION_ROLE_FALLBACK_DEFAULT is the zero role, so cleared storage is already presentable. The SDL frontend records the role its last present selected and branches on no Core enum. Animation and frame selection stays with PBI-053, so E13 stays open |
| 2026-07-22 | E13, PBI-053 | Added deterministic manifest animation and frame selection to components/presentation, closing E13 | ./gate.py strict passed over headless with 13 public headers checked and ./gate.py strict --app desktop passed with 8 CTest cases and 17 public headers checked. ./gate.py fast --all-compositions, 31 composition tests, 26 gate tests, the 17 pet_presentation_tests cases, and the 31 pet_frontend_sdl_tests cases passed | Selection takes an accepted PetManifest, a role, and an explicit animation time, and answers with manifest role, animation, frame, and image indexes plus the frame duration, so a renderer needs no identifier and no Core enum. A multi-frame animation loops over its total duration, a missing role or broken chain selects the manifest fallback role, and storage no parse produced is answered rather than trusted. The SDL frontend keeps the accepted manifest beside the images it decoded, so presenting selects without reading a file. Drawing stays with E14 |
| 2026-07-22 | PBI-054 | Gave the SDL frontend a real window and renderer lifecycle and a guaranteed fallback render path | ./gate.py strict --app desktop passed with 9 CTest cases and 17 public headers checked, ./gate.py strict passed over headless, ./gate.py fast --all-compositions, 31 composition tests, 26 gate tests, the 18 pet_frontend_sdl_tests frontend cases, the 15 asset cases, and the 2 pet_frontend_sdl_renderer_tests cases passed, under GCC, Clang, ASan, and UBSan. pet_desktop also ran 120 frames against the real Wayland driver and exited zero, and a run against the X11 driver was observed on screen as a 320x240 pet window with the dimmed background and the centred body | Creating opens the window and the renderer, presenting clears, draws the built-in fallback, and presents, and destroying releases both. PET_FRONTEND_SDL_ERR_RENDERER is the new status and no SDL type reaches the public header. CTest gives every test that creates a frontend SDL_VIDEODRIVER=dummy, and the refused renderer is a second executable because SDL copies the environment once per process. Events are pumped and dropped, recorded as LIM-009. Layout, scaling, and asset-backed drawing stay with PBI-055 and PBI-056, so E14 stays open |
| 2026-07-22 | PBI-055 | Added the shared components/layout canvas fit policy and made the SDL fallback frame consume it | ./gate.py strict --app desktop passed with 10 CTest cases and 18 public headers checked, ./gate.py strict passed over headless with 7 CTest cases and 14 public headers checked, ./gate.py fast --all-compositions, the 10 pet_layout_tests cases, the 20 pet_frontend_sdl_tests frontend cases, the 15 asset cases, the 2 pet_frontend_sdl_renderer_tests cases, pet_desktop_tests, and pet_desktop_scenario passed, under GCC, Clang, ASan, and UBSan | pet_layout_fit centres the canvas, preserves its aspect ratio, floors the scaled extents, and reports the whole multiple when there is one, with 64-bit intermediates and no allocation. The release logical canvas is 320 by 240 and the window is now resizable, so the fit answers a real output size on every frame. pet_frontend_sdl_last_canvas reports the placement, which is what lets an automated test assert the shared answer without reaching SDL. Asset-backed drawing stays with PBI-056, so E14 stays open |
| 2026-07-22 | E14, PBI-056 | Drew the selected asset frame through the shared layout, closing E14 | ./gate.py strict --app desktop passed with 10 CTest cases and 18 public headers checked, ./gate.py fast --all-compositions passed, the 20 pet_frontend_sdl_tests frontend cases, the 16 asset cases, the 2 pet_frontend_sdl_renderer_tests cases, pet_layout_tests, pet_desktop_tests, and pet_desktop_scenario passed, under GCC, Clang, ASan, and UBSan. pet_desktop also ran 400 frames of the canonical set on the real driver and exited zero | A stored image resource is now a renderer-owned texture rather than a surface: the surface is the decoding step and is released as soon as the texture exists, with nearest sampling so an upscaled canvas keeps its pixels. The public lifecycle is unchanged and no SDL type reaches the public header. Presenting draws the selected frame over the placed canvas when an accepted set holds it, and the built-in fallback otherwise, so the fallback guarantee is unchanged. Source rectangles, atlases, and per-image fit stay out of this release |
| 2026-07-24 | PBI-057 | Defined and implemented the SDL frontend event contract that classifies platform input before it can reach the Core | ./gate.py strict --app desktop passed with 10 CTest cases and 18 public headers checked, under GCC, Clang, ASan, and UBSan, and the 20 pet_frontend_sdl_tests frontend cases, the 16 asset cases, the 9 new event cases, and the 2 pet_frontend_sdl_renderer_tests cases passed | pet_frontend_sdl_poll_event reports a classified PetFrontendSdlEvent: quit and window close are host control, resize and focus are frontend events, and everything else is ignored, so no raw SDL_Event reaches an app. The payload can carry a supported PetAction but no path produces one yet. Presenting pumps but no longer flushes the queue, so an event survives to be polled. Input mapping stays with PBI-058 and app dispatch with PBI-059, so E15 stays open and LIM-009 stays open now tracking PBI-059 |
| 2026-07-24 | PBI-058 | Mapped a space key and a primary mouse button to PET_ACTION_GREET through the frontend event contract | ./gate.py strict --app desktop passed with 10 CTest cases and 18 public headers checked, under GCC, Clang, ASan, and UBSan, and the 20 pet_frontend_sdl_tests frontend cases, the 16 asset cases, the 13 event cases, and the 2 pet_frontend_sdl_renderer_tests cases passed | A non-repeat SDL_EVENT_KEY_DOWN on SDLK_SPACE and a SDL_EVENT_MOUSE_BUTTON_DOWN on SDL_BUTTON_LEFT classify as PET_FRONTEND_SDL_EVENT_ACTION carrying PET_ACTION_GREET. Key repeat, releases, and other keys and buttons stay ignored, so one press is one action with no rapid repeated input. No Core function is called and no Core action is invented. App dispatch stays with PBI-059, so E15 stays open and LIM-009 stays open tracking PBI-059 |
| 2026-07-24 | E15, PBI-059 | Dispatched semantic input through the desktop app and added a live interactive run mode, closing E15 | ./gate.py strict --app desktop passed with 10 CTest cases and 19 public headers checked, under GCC, Clang, ASan, and UBSan, and the 10 diagnostics, 7 clock, 11 lifecycle, and 7 new input pet_desktop_tests cases, pet_desktop_scenario, and the pet_frontend_sdl_tests cases passed. Manual runs against the real OpenGL driver confirmed a window, a live loop, and clean exit on Ctrl-C and SIGTERM | pet_desktop drains pet_frontend_sdl_poll_event each loop turn, dispatches only a PET_FRONTEND_SDL_EVENT_ACTION through pet_world_apply_action, and ends the loop on a host control quit, stopping the drain at once so an action queued behind a quit never mutates the Core, so a space press or left click greets the pet and the window close exits cleanly. --until-quit runs live with real-clock deltas and about sixty frames a second of pacing, while --steps stays deterministic. The app consumes only PetFrontendSdlEvent, so no SDL type reaches it. LIM-009 is closed with real-driver evidence. Ctrl-C stops the process because SDL turns it into the quit event, so no Ctrl-C bug was recorded |
| 2026-07-24 | PBI-060 | Added the project verification policy under tools/config/ and the include boundary, asset, and Core dependency checks that read it | ./gate.py strict passed over headless with 7 CTest cases and 14 public headers checked, ./gate.py strict --app desktop passed with 10 CTest cases and 19 public headers checked, and ./gate.py fast passed over both compositions. The 69 test_verification.py cases, 29 gate tests, and 31 composition tests passed, and boundaries.py, assets.py, and symbols.py were each run alone against both compositions. Live negative runs proved a Core SDL include, an app Core private header include, a missing runtime asset, a canonical asset that only the build output holds, a malformed config, and an undeclared toolchain all fail with a diagnostic and a non-zero status | tools/config/verification.json names the roots, the SDL dependency family, the include boundaries, the Core symbol allowance, and the pet asset set once, so a check reads policy instead of carrying a copy. Sprite names are read from the manifest rather than listed, and a reference the build output satisfies while the source tree does not is a finding, which is how a generated artefact is kept from becoming canonical. The symbol scan now takes --build-dir and also reads the Core compile lines, so an SDL dependency is caught before any SDL symbol is used. E16 stays open with PBI-061 through PBI-063 |
| 2026-07-24 | PBI-061 | Extended the desktop process scenario so each lifecycle stage it reaches is asserted, including a real frontend failure path | ctest -R pet_desktop_scenario passed, ./gate.py fast --app desktop passed with 10 CTest cases, and ./gate.py strict --app desktop passed with the scenario running under GCC, Clang, ASan, and UBSan and 19 public headers checked. Two mutated copies of the scenario, one expecting the wrong frame count and one the wrong failure exit status, both failed, so the new assertions are not passing vacuously | The scenario now separates Core initialisation, frontend initialisation, the update and render path, and clean shutdown, and adds a run with an unusable video driver that must end with exit status 3 and the frontend diagnostic contract, which is what proves the process reached frontend initialisation after the Core initialised. The environment override is per run, so the CTest SDL_VIDEODRIVER=dummy property is unchanged. Only the scenario and one CMake comment changed, and no production code was touched. Display and input automation stay unavailable and are recorded as LIM-011. E16 stays open with PBI-062 and PBI-063 |
| 2026-07-24 | PBI-062 | Verified every supported v0.2.0 composition and recorded the Linux desktop evidence without widening a support claim | ./gate.py strict passed over headless with 7 CTest cases and 14 public headers, ./gate.py strict --app desktop passed with 10 CTest cases and 19 public headers under GCC, Clang, ASan, and UBSan, ./gate.py fast --all-compositions passed over both compositions, ctest -R pet_desktop_scenario passed, and test_verification.py, boundaries.py, assets.py, symbols.py, and matrix.py each passed when run alone. pet_desktop --steps 240 --step-ms 16 exited zero against the real Wayland driver and again against X11, and the maintainer manually observed the drawn pet, a greet response to the space key and the left mouse button, and a clean exit on both drivers | No source, build, test, or tooling file changed and no commit is suggested. An uncommitted docs/ ignore rule was removed from .gitignore, so release documents are visible in git status while staying out of every commit. Linux GCC and Clang are verified, Windows stays designed and unverified under LIM-001 and LIM-002, and embedded becomes LIM-012 as an architectural hypothesis. LIM-011 stays open, acknowledged with real-driver process evidence and manual observation of rendering and interaction and carried to PBI-063, because no automated check covers either, and LIM-005 moves off this PBI to PBI-063 and a future dependency PBI because no gate run needed the network. The audit gate is left to PBI-063, which runs it against the commit it tags, so E16 stays open |
| 2026-07-24 | PBI-063, E16 | Completed the v0.2.0 release review, closing E16 and the release | ./gate.py audit --all-compositions passed over headless and desktop-sdl under GCC, Clang, ASan, and UBSan, with Core coverage 100% lines and branches (143/143 and 94/94), the allocation probe at 0 allocations and 0 errors, Valgrind clean with 13 framework allocations, Cppcheck over 62 sources, and 14 headless and 19 desktop-sdl public headers. python3 tools/verify/matrix.py recorded the toolchain and git diff --check was clean | Bumped the project version to 0.2.0 and finalised arch-v0.2.0.md as Accepted with a release evidence section. Ticked the v0.2.0 milestone exit criteria and E16 completion criteria, and marked E15 and E16 completed in the epic map. Created the docs/releases/v0.2.0/ archive with README, evidence, and snapshots, then rolled the active planning documents over to continue at E17 and PBI-064. Linux GCC and Clang are verified, Windows stays designed under LIM-001 and LIM-002, and embedded stays designed under LIM-012. No git tag, branch, or push was created, which stays a maintainer step |