# Verification and release gates

[Guide](guide.md) · [Patterns](patterns.md) · [Engines](engines.md) · [Handoff](templates/handoff.md)

**Prove the visible behavior, the stable fallback and the resource lifecycle separately.** A successful import, API probe or animation callback is not evidence that the intended pixels appeared. A browser test is not human art direction, screen-reader certification or real-mobile validation.

This is a verification contract for adopting projects. The [capability catalog](capability-catalog.json) records historical lab scope separately from repository specimens; it is not a report that every gate below passed in this checkout. Use actual repository test output for the integrated M15–M18 specimens. Preserve failures, skips and device blockers as distinct outcomes.

## Declare the acceptance conditions before testing

For each primitive, name:

- semantic input, valid range, initial/current/settled/error state;
- property/resource owner and time units;
- expected intermediate output and how it is measured;
- reset/interruption behavior, live reduced-motion policy and no-JS result;
- keyboard/focus contract and target viewports/devices;
- loading/network/asset fallback and cleanup boundary;
- payload, frame-work and lifecycle budgets, with method and test conditions.

Run a deliberately failing behavioral slice before implementation when changing code. A meaningful negative control removes the behavior under test and causes the assertion to fail. Do not weaken the assertion after seeing results just to label the build green.

## State and output matrix

| Case | Required observation | Common false positive |
|---|---|---|
| Primary action | Correct host state, visible intermediate effect, correct settled result | Status text changed while the animation never rendered |
| Changed context | Long/localized copy, changed item count/input and resize remain valid | Replaying the same fixture called transfer testing |
| Interruption | New intent wins; reset cannot be undone by a stale callback | Only testing after animation has finished |
| Reduced at load | Complete static composition; no unnecessary work starts | CSS duration shortened while a JS loop continues |
| Reduced midflight | Immediate intentional settlement, preserved focus/current content | Replacing a focused subtree to make the screenshot static |
| Missing JS/API | Essential content and normal navigation remain; unavailable enhancements are honest | A headline is visible but the rest of the explanation is hidden |
| Missing asset/renderer | Current semantic state and meaningful fallback survive | Infinite loader, blank canvas or silent CDN fallback |
| Release/remount | No stale input, callbacks, tickers or owned resource activity | Detached DOM alone mistaken for zero-memory-leak proof |

### Primitive-specific assertions

- **M15:** observe an interior progress/dash value, completion and explicit reset; reset during acquisition, repeat activation, and deny WAAPI. Ensure no retained running animation and no animation-driven false claim of operational success.
- **M16:** retain the same DOM item identities, verify actual reading order, an interior FLIP displacement and settled rectangles; rapidly reverse, change item count/labels, and cross a breakpoint. Focus stays with the actual retained node.
- **M17:** verify the original semantic text/accessible name, actual glyph family and final weight; test grapheme clusters, wrapping, changed language/copy midflight and blocked fonts. No stale visual copy, interactive clone or repeated announcement survives.
- **M18:** sample the native range's endpoints and interior; verify both aperture and marker derive from the same normalized value. Invalid values cannot corrupt the last valid pose. Direct mapping has no autonomous loop; reduced mode still exposes the same relationship.

Use deterministic sampling where possible: set controlled animation time or wait for a measured interior condition rather than relying only on wall-clock sleeps. Pair DOM/geometry assertions with actual rendered inspection; pixel hashes alone need a stated tolerance and rasterizer context.

## Lifecycle: pause is not destroy

Use an explicit lifecycle model such as `idle → running ↔ suspended → destroyed`. Track the reasons for suspension independently: user pause, reduced policy, offscreen, hidden and cached navigation. Removing one reason must not erase another. One-shot renderers should never create a perpetual loop in the first place.

**Visibility:** stop owned advancement when hidden/offscreen. On return, update from valid host state and reset stale wall-clock deltas; do not replay missed work or begin a long catch-up. Verify the actual hidden-tab path as well as viewport intersection. Synthetic events test handlers, not browser lifecycle behavior.

**Back/forward cache:** `pagehide` is compatible with bfcache.[68] On `pagehide.persisted`, suspend recoverable resources rather than terminally destroying the component. On persisted `pageshow`, restore the same selection, manual pose/scroll and focused node; respect explicit pause. Never automatically resume sound. A nonpersisted departure/unmount can use terminal teardown.

A cache test must navigate away and Back and record actual `pageshow.persisted` or equivalent browser evidence. Remove a harness's bfcache-disabling default when necessary. An ordinary reload, synthetic `PageTransitionEvent` or unproven Back is not a cache pass. If the browser declines cache eligibility, report the reason and mark the case skipped/blocked; separately test the noncached restoration path.

**Async races:** delay asset/WASM/worker initialization, switch twice, then release. A stale result must dispose itself, not seize the canvas or update new content. Use generation IDs or abort signals; include failures after cancellation rather than swallowing every rejection.

**Teardown inventory:** cancel owned WAAPI and frame/ticker handles; clear timers; unsubscribe media/visibility/input observers; remove transient text wrappers; terminate workers; destroy owned textures/buffers/contexts/worlds; unload audio resources. Closing an application-shared audio/GPU context is a bug, not thorough cleanup. Run repeated mount/action/destroy cycles and verify the count of live owned resources returns to baseline.

## Content, focus, fonts and access

Test keyboard use end to end: Tab order, Enter/Space, range keys, dismissal/reset, anchors, scrollable regions and visible focus. Preserve the focused node across reduced-motion changes and semantic reorders. Before removing a focused panel or frame, move focus to its documented trigger or successor. Never use a screen snapshot as the focus source of truth.

Inspect the accessible tree and use a real screen-reader review for production-critical interactions. Decorative visual glyphs and duplicate SVG labels must not duplicate essential announcements. Do not announce every animation frame. Check actual hit targets and contrast; an automated accessibility scan does not cover all of them.

**Fonts need two proofs:** the face loaded, and the intended glyphs actually rendered. `document.fonts.ready` and `document.fonts.check()` are useful but insufficient when a downloaded optional font leaves fallback glyphs on screen. Inspect rendered fonts (for example, Chromium's platform-font query for the text node), cold load behavior and line wrapping. Verify the actual asset's axes instead of assuming a variable font supports all registered axis types.

Deny font requests in a test: the fallback must preserve readable layout and must not leave M17 waiting forever. Preload only the allowed above-fold fonts, using exact URLs and `crossorigin`; use the licensed self-hosted assets. Inspect requests for PPNM and font CDNs rather than trusting the stylesheet's intent.

Test 390px narrow-phone, **700px wide-phone**, and a desktop width, plus either side of layout changes at the canonical 640/768/1024/1200 breakpoints. Check zoom, long labels, orientation changes, print and horizontal overflow. Emulated width is a layout test, not evidence for touch latency, heat, memory pressure or mobile battery use.

## Negative tests that can falsify the result

1. Block the actual JS bundle and reload. Do not merely set a developer “failed” flag.
2. Deny the relevant font, image, vector asset and WASM independently; deny outside requests to catch fallback CDNs.
3. Remove a typed-property registration in a test response when interpolation is claimed. Require the intermediate-value test to fail rather than accepting a final application assignment.
4. Disable or force rejection of the exact animation/renderer API. Label this **forced-unavailable**, distinct from a real unsupported device.
5. Inject nonfinite, out-of-range and malformed inputs without replacing the last good state.
6. Trigger actual WebGL context loss, failed GPU adapter/device acquisition or a delayed worker reply for the adopted graphics path.
7. Toggle motion preference while a panel is exiting or a font/asset is loading. Preserve current semantics and focus.
8. Release during pending work, then deliver old events. No old DOM/state/scroll may change.

Keep an allowlist of **expected injected failures**, separate from unexpected console/network errors. Happy-path acceptance requires no unexpected errors; negative tests require the specific fallback, not a blanket ignored error log.

## Measured performance gates

Set project budgets **before** testing. The DSM does not claim a universal renderer/particle threshold or an already-passed mobile budget. A target without its units, conditions and measurement method is not a useful gate.

| Metric | Method and record | Acceptance rule |
|---|---|---|
| Initial/optional bytes | Actual transfer and decoded asset inventory; cold/warm cache distinguished | Essential content has no optional renderer dependency; each declared payload cap passes |
| Startup | Navigation/marks around import, asset decode and first useful output | Compare against the predeclared cap on named device/browser; separate network from CPU |
| Interaction work | Trace input → host update → visual frame; report long tasks and frame-work distribution | State acknowledgment remains prompt; predeclared percentile/max work budget passes |
| Continuous rendering | Record viewport, DPR, backend, scene count and sample interval | Meet the declared sustained budget; stop or reduce nonessential work when the agreed fallback threshold is crossed |
| Simulation | Fixed-step count, catch-up cap and discarded elapsed time | No unbounded catch-up or nonfinite states; units and stable fixture checked |
| Lifecycle | Owned-resource census before/after cycles; CPU/work when static/hidden | No continuing owned work after release; no unexplained growth over the declared run |
| GPU | Actual adapter/backend, readback or timestamp method if supported | Valid output first; no “GPU speedup” claim without a comparable measured baseline |

rAF intervals are not GPU timings. Fast one-shot contour output is not continuous heavy-scene throughput. API availability is not execution. For WebGPU compute, read back every expected output and compare with a CPU oracle; do not infer device identity from an unrelated WebGL string. For audio, sample analysis is not listening quality.

If a budget fails, reduce pixel area, passes, texture size, object count or optional motion; then rerun the same fixture and report the visual compromise. An adaptive-quality claim requires exercising the governor through pressure, downgrade and recovery—not just printing frame statistics. Missing physical-device access remains a release limitation, not a fabricated pass.

## Portable delivery and acceptance packet

Test the assembled artifact at its actual mounted path, including a relocated/subpath delivery where supported. Inspect relative assets, anchor destinations, MIME types, license notices and blocked-network behavior. A development server at `/` alone is insufficient. Reproduce from clean source and lockfiles separately from a warm checkout rerun; record exactly which occurred.

Deliver:

- source/runtime revisions and rights inventory;
- brief plus primitive/property ownership map;
- behavioral/output/lifecycle results with expected faults separated;
- full/reduced/no-JS reference states and inspected viewport/font evidence;
- measured budgets with browser/version, device/OS, viewport/DPR, cache state, backend, fixture and method;
- human design/accessibility/device reviews, or clearly named outstanding gates;
- exact commands and relative evidence paths, refresh triggers and known limitations.

A release report states what changed, what the tools actually verified and what remains unverified. Do not promote documentary editor recipes, historical lab tests or another browser's output into current repository proof.

## Sources

[68] https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
