--- name: makepad-qa-release description: "Verify and ship Makepad games: cargo check/test/clippy, makepad-test driven runs with real input and screenshots, headless probes, bot playtests, performance evidence against BUDGETS, cross-platform and XR checks, and packaging with makepad-game-pkg. Use for QA, testing, verification, evidence, regression, release build, packaging and 'is it actually working' requests." --- # Makepad QA & Release Turn claims into evidence. The deliverable of this skill is a report someone else can check. ## The ladder Climb only as far as the change warrants; never skip a lower rung. | # | Rung | Command | Proves | | --- | --- | --- | --- | | 1 | Compiles | `cargo check -p ` | syntax and types. **Not** the splash DSL | | 2 | Lints | `cargo clippy -p ` | repo `clippy.toml` conventions | | 3 | Unit | `cargo test -p ` | rules, in Cx-free crates | | 4 | Driven | `cargo test -p makepad-arcade` with `makepad-test` | the real app under real input | | 5 | Seen | `app.screenshot()` | what it actually looks like | | 6 | Measured | `frame_p90_ms()`, probe examples | it holds frame time | | 7 | Shipped | `cargo build --release` | it builds for delivery | Required by change size: | Change | Rungs | | --- | --- | | Constant, copy, colour | 1 | | Rule in a Cx-free crate | 1, 3 | | New mechanic | 1, 3, 4 | | Any UI or visual change | 1, 4, 5 | | Performance work | 1, 6 (before **and** after) | | Full game / premium pass | all, plus the scorecard | **Rung 1 does not cover the splash DSL.** It resolves at runtime, so a style or widget error passes `cargo check` and fails on launch. Any UI change requires rung 4 at minimum. ## Unit testing The architecture exists to make this possible — `sim`, `gen`, `blocks`, `audio`, `net`, `coedit`, `pkg`, `assets` need no `Cx`. ```bash cargo test -p makepad-game-sim cargo test -p makepad-game-gen cargo test -p makepad-game-blocks cargo test -p makepad-game-net ``` Assert on observable behaviour, not internals: the mover clears a 0.4 m step and not a 1.5 m wall; the lap counts once per crossing; the level generator's goal is reachable from its spawn; the seeded generator produces byte-identical output twice. **Test compositions as compositions.** Two half-tests in two crates can both pass while the car floats above the road — which is why `blocks` dev-depends on `gen` and `render`. If the claim spans crates, the test must too. ## Driven testing ```rust use makepad_test::{makepad_test, TestApp, KeyCode, Selector}; #[makepad_test] fn player_can_win(app: TestApp) { app.wait_for_log_contains("game: ready"); app.press_key(KeyCode::ArrowUp); app.wait_for_log_contains("race: won"); println!("evidence: {}", app.screenshot().display()); } ``` `TestApp` gives `type_text`, `press_return`, `press_key`, `press_key_with_modifiers`, `touch_down`, `screenshot`, `widget_dump`, `widget_snapshot`, `wait_for_log_contains`, `locator(Selector)`, `forward(msgs)`. Every method has a `try_` variant returning `TestResult`. **Synchronise on logs, never on sleeps.** `wait_for_log_contains` is why the game should log `game: ready`, `state: paused`, `race: won`. One log line per state transition makes the whole flow machine-checkable. ## Bot playtest A scripted player that plays the loop end to end, catching what unit tests cannot: unwinnable levels, softlocks, and slow leaks. Cover: reach the win state; reach the lose state; play 5+ minutes without a crash, memory growth, or frame-time drift; win twice in a row (catches incomplete `reset()`); pause/resume mid-run; and, if networked, two sessions staying in sync. On the script route, drive the bot with the same verbs (`autodrive`, `walk`, `chase`) rather than synthesising input events. ## Screenshots Capture every state — menu, playing, paused, win, lose — at the **target aspect ratio**, and at each supported ratio (16:9, 4:3, tall phone) for UI claims. Then **look at them and describe what you saw.** An agent that captures a file and does not inspect it has produced an artefact, not evidence. Name them for content: `artifacts/shots/playing-desert-1920x1080.png`. ## Performance evidence ```bash cargo run -p makepad-arcade --example bigworld_probe --release ``` Report p90 in release on a named device, before and after, against `apps/arcade/BUDGETS.md`. Include `quality_level()` and `quality_reason()` — a good frame time achieved by adaptive quality silently dropping to q1 is not a good frame time. ## Asset verification Assets degrade silently to primitives, so a screenshot cannot prove they loaded: ```rust for id in scene_ids { assert!(renderer.model_is_loaded(id), "missing {id}"); } ``` Also confirm asset-dependent tests **ran** rather than skipped — without the library they skip with a hint, and a skipped test reported as passing is a false report. ## Cross-platform Do not claim a platform you have not run. Desktop (macOS/Windows/Linux), Android (this fork's branch targets it), web (memory pressure is the constraint — see the fork's Route work), and Quest/XR (72 Hz, bandwidth-bound, comfort rules). For each claimed platform state whether you ran it or only built it. ## Release 1. `cargo build --release` for each target 2. Full ladder green 3. Scorecard ≥ 2.3, nothing below 2, for a premium claim 4. Credits present — `resources/CREDITS.toml`, so a package carries attribution 5. Package via `makepad-game-pkg`: `Manifest`, `Library`, `Registry`, `sha256` / `verify_digest`. Content-address and verify; a package whose digest is unverified is untrusted input. ## Reporting Lead with the outcome. Then evidence, with commands and what they printed. Then remaining risks. State plainly what you did **not** run — an unrun check reported as passing is the single failure that invalidates the rest of the report. ## Required reading - `references/test-harness.md` — writing unit and driven tests here. - `references/release-checks.md` — the pre-ship checklist and packaging.