When an rn-fast-runner / rn-android-runner HTTP response is lost after a command was sent (timeout, socket reset, runner busy), the client cannot tell "never executed" from "executed, response lost". Today that ambiguity costs either a session teardown (re-paying runner boot, cf. #382) or a blind retry that risks a double-fired mutation — the tap lands twice and the flow silently diverges.
Approach:
- Runner keeps a small bounded journal of recent command outcomes keyed by
commandId — small scalar results retained, snapshots/screenshots explicitly excluded from retention.
- On an ambiguous post-send failure the client issues one short-budget
status(commandId) probe before invalidating: completed ⇒ return the recovered result (no teardown, no resend); failed ⇒ surface the runner's error; unknown/timeout ⇒ existing invalidation path unchanged.
- Mutating verbs (tap/longpress/fill/swipe/scroll) are never auto-resent after an observed accepted/started; read-only verbs keep existing retry.
- Follow-on: skip the per-command readiness preflight when a healthy mutating response for the same app landed ≤5s ago; recency lives on the session object, dies with invalidation, and clears on any transport failure.
- Diagnostics record per command whether a preflight/probe was used, skipped, or recovered — so a fast path failing stays debuggable.
Measurement gate: injected lost-response tests recover without session teardown; zero duplicated mutations across the suite; hot interaction loops save one runner round-trip per command once the recency skip lands.
Impact: session stability + no double-fired taps · Effort: M · Depends: #385 (capability flags); sits one layer below #386 (tap-level healing)
When an rn-fast-runner / rn-android-runner HTTP response is lost after a command was sent (timeout, socket reset, runner busy), the client cannot tell "never executed" from "executed, response lost". Today that ambiguity costs either a session teardown (re-paying runner boot, cf. #382) or a blind retry that risks a double-fired mutation — the tap lands twice and the flow silently diverges.
Approach:
commandId— small scalar results retained, snapshots/screenshots explicitly excluded from retention.status(commandId)probe before invalidating:completed⇒ return the recovered result (no teardown, no resend);failed⇒ surface the runner's error; unknown/timeout ⇒ existing invalidation path unchanged.Measurement gate: injected lost-response tests recover without session teardown; zero duplicated mutations across the suite; hot interaction loops save one runner round-trip per command once the recency skip lands.
Impact: session stability + no double-fired taps · Effort: M · Depends: #385 (capability flags); sits one layer below #386 (tap-level healing)