Skip to content

Prototype user-test sessions

prototype actions testSessionBegin {id, goal?, cohort?}, testSessionEnd {id} and testSessionReport {id?, testSessionId?} operate on the host without device requests or device ownership acquisition. Begin allows one active recording per device and prototype; routing session ownership scopes recorded events and latest-session lookup. An explicit test-session id reads that stored session across routing sessions and devices, so reports remain accessible after release or reconnect. Cohort is metadata, without cross-session aggregation. A goal is a screen label; completion remains true once reached, even if the user navigates away.

The coordinator records only events accepted by its sequence buffer, independently of event consumption. The recorder sanitizes synchronously and serializes database writes, begin/end and reports on one promise chain. Failures are logged and do not interrupt device event delivery. End flushes earlier writes. Every step is stamped with the host’s receive time, never the device’s event timestamp, because a physical device clock can be skewed from the host’s. A daemon restart does not resume event recording: stored sessions remain reportable, and a begin for the same device/id ends any active row this daemon does not record (reason released). Events are recorded only for a prototype shown under the beginning call’s session scope.

SQLite tables prototype_test_sessions and prototype_test_session_steps retain session metadata and ordered sanitized steps. At most 5,000 steps are stored per session; extra steps increment dropped_count. Pruning keeps the 50 newest ended sessions per device/prototype and deletes their events with them. Active sessions are retained. A truncated report describes retained events; later navigation and goal arrivals may be absent, so completion and abandonment can be incomplete.

No state snapshots, text values, context, arbitrary payloads or data-write values are stored. Input changes keep non-secure keys, string lengths or scalar types and validity. Secure fields are omitted, including keys in validation failures. The spec walker covers components, repeated templates and slots. Unresolved secure keys or an unavailable spec suppress all field keys conservatively. Data writes keep only path and operation; other authored emits keep their name.

Screen labels select the deepest stack path’s top screen, ties by lexicographic stack id, or (root) without stacks. Reports count visits and exits in first-visit order; all events’ own stack snapshots determine the current screen. Time starts at session begin and moves at step receive times, clamped monotonically to the session boundary. The final visit ends at end/dismissal, or report time while active. An ended session that has not reached its goal is abandoned at its final screen, unless the daemon ended it (released); an unset goal reports completion as null.

window.deadTaps: true requires prototype_dead_tap_v1; show refuses older agents before mutation. The reserved prototype_ emit namespace prevents authored events from impersonating dead taps. Validated dead-tap coordinates are window-local dp (points on iOS), rounded by the agent to one decimal. Reports retain up to 200 points per screen and count every retained tap in a 48-unit grid. Android measures from the prototype’s own window; iOS measures from the agent window, which fills the scene, so non-fullscreen iOS placements are offset by the placement origin (not device-verified). Screenshot heatmap overlays are not implemented. testSession.deadTaps reflects the current shown spec; absent or forgotten specs report off, including after dismissal.