OpenBox 0.9.0 is the engineering foundation release: the same launcher, rebuilt so every change ships with proof. The backend now has a route registry with structured errors and request ids, a versioned API surface, and gzip-served libraries that load 96% smaller; the browser UI is split into static assets with centralized state and a persistent error banner. Plus the full LaunchBox media catalog, chrome-less app windows, rolling state snapshots, and release signing tooling.
Engineering foundation
make checkruns lint, compile checks, all 45 test files, and coverage floors in one command. CI enforces it on push, pull requests, and a weekly schedule, and a version-sync check fails whenupdates.pydisagrees with README, metainfo, PARITY.md, or the bug template.- Fixed latent undefined names in
web_app.py(automation.DEFAULT_*,contained_path,read_limited) that would have crashed their code paths on first use.
Backend
- The two monolithic HTTP dispatch chains (613-line GET, 195-line POST) became a route registry in
routes.py: 60 GET and 90 POST entries, each a named handler. - Errors are structured: every failure carries a stable machine code (
GAME_NOT_FOUND,MEDIA_JOB_RUNNING, ...) plus a request id that appears in the UI and the diagnostic log. /api/v1/*is the stable contract; legacy paths keep working.scripts/gen_api_docs.pygenerates the reference page from the live tables.- The library payload is gzip-compressed once per state change and served with conditional GET: 5,000 games respond in 1.9ms at 638KB instead of 13.8MB.
- Settings saves drop unknown keys instead of persisting junk, and the state store keeps rolling snapshots with dry-run recovery so a bad edit can be rolled back to a point in time.
- Background jobs keep a bounded history, surfaced at
/api/jobsand in the Library Audit dialog. - Ctrl-C / SIGTERM now stops running sessions gracefully, and the session poll slows to 10s when nothing is running.
Frontend
index.htmlshrank from 3,117 lines to a 485-line shell; JS and CSS live instatic/and are served with cache headers.- All 30 browser state globals moved into one
AppStateobject; UI preferences persist throughlocalStorage. - Server errors surface in a dismissible banner with a "Copy details" action that includes the request id, so bug reports come pre-correlated.
- Accessibility pass: dialogs are
aria-modal, toasts and lifecycle messages are live regions, and the label floor rose to 12px with a stronger library heading.
Media and metadata
- Full LaunchBox media catalog downloads: box backs, box spines, 3D boxes, clear logos, fanart, banners, title screens, cart fronts, cart backs, discs, and advertisement flyers, alongside covers, backgrounds, and screenshots.
- Manuals are not in the LaunchBox feed, so the manual option pulls a PDF or text manual out of the game's own archive and reports when none was found.
- Platform name mapping ranks exact LaunchBox matches first (Game Boy, PlayStation, GameCube, Xbox, and friends).
- The desktop UI opens chrome-less by default with
--app-window/--no-app-windowoverrides and a Settings option.
Reliability and supply chain
- A 23-scenario reliability catalog documents expected behavior for corrupt state, full disks, orphaned sessions, wrong credentials, and huge archives; see
docs/reliability.mdandSUPPORT.md. - Releases get a CycloneDX SBOM and optional Ed25519 signatures with a standard-library verifier;
scripts/release.shruns the whole pipeline up to the final publish step.
Verification
- Ran
./run_all_tests.sh: 45 test files, 0 failures. - Ran
make check: lint, compile, tests, and coverage gates pass (56% total, 44% web_app.py). - UI smoke test boots a real server and drives the grid with no page errors.
Full Changelog: v0.8.2...v0.9.0