One Vocabulary, Two Surfaces
This chapter examines the engine boundary that lets tester-army/e2e use one semantic test model across browsers and native mobile apps. The question is not whether a browser and a phone behave identically; they do not. The useful judgment is which parts of a test belong to the shared runner, which depend on an engine capability or platform fixture, and why explicit validation is safer than silently pretending that every surface can do everything.
The previous checkpoint treated an e2e step as an evidence chain. An engine is the part of the system that connects that chain to a real surface: it observes the app, locates targets, performs actions, manages resources, and reports artifacts. Core e2e remains model-free with respect to that platform work. The same agent and assertion layers can sit above a web engine or a mobile engine because both speak a contract designed around meaning rather than a browser or device noun.
The engine boundary is a capability contract
The e2e/engine contract describes what a target can expose. An engine may provide semantic observation, node actions, locator resolution, point actions, keyboard input, state capture, artifacts, lifecycle hooks, and contributed fixtures. These are capabilities, not assumptions. An engine that can observe but cannot locate cannot support the same deterministic screen layer as one that implements both. An engine that can tap but cannot provide a secure screenshot should not be offered visual evidence it cannot protect.
This design keeps the core runner independent of Playwright, agent-device, or any future surface. The runner knows how to account for a step, apply a timeout, build a locator expression, enforce strictness, record a report, and route a capability error. The engine knows how to capture a semantic tree or device snapshot and how to turn a validated action into a platform operation. The model knows neither implementation. It sees the common observation and grammar.
The seam is made strict at configuration time. defineEngine checks the engine manifest and rejects unknown keys, incompatible contract versions, incomplete action declarations, and dependencies that do not make sense. For example, an engine cannot declare perform without observation, because an action target comes from an observation. It cannot declare action kinds without the implementation that performs them. A malformed engine fails as configuration rather than quietly dropping to a less capable mode.
That early failure is an architectural choice with operational consequences. Silent fallback would make a test appear portable while changing what it can prove. If a device engine quietly ignored a browser-only action, the model might continue toward a different outcome. If a target declared state support but its restore hook merged old state instead of replacing it, authenticated tests could leak state between attempts. Capability validation makes the boundary visible before a run reaches an ambiguous UI.
The contract also gives each capability an owner. Observation owns the current semantic snapshot. Location resolves a query against that snapshot. perform carries out one node action, while point actions handle controls that the tree cannot name but pixels can locate. Keyboard input is separate because focused typing does not always have a node target. State capture and restore are opaque engine services, while artifacts provide screenshots, traces, and videos under the attempt’s evidence policy. Lifecycle hooks prepare resources, isolate attempts, settle late work, and dispose of the surface. This separation prevents a browser or device implementation from quietly smuggling platform behavior into the core runner.
The distinction is useful when a capability is partial. An engine can support observation and node taps but not pointer actions. It can expose a screenshot only when secure regions are maskable.