Repository navigation
iOS: decode the targetActivation stamp for commands that consume no capture (press <x> <y>, live @ref) #2694
Description
Activity
- added 12 commits that reference this issue
on Sep 19, 2026 Second member of this family, found while verifying #2693 on a simulator with the reviewer's own handoff (
xcrun simctl openurl <UDID> https://example.com, Safari foreground, then one command):$ agent-device wait stable 4000 # capture-consuming Warning: The session app was not foreground when this command arrived (prior state runningBackground), … $ agent-device wait text "Example" # findText-based (nothing)The runner did pay for the repair in both cases.
.findTextreturnsResponse(ok: true, data: DataPayload(found: found))(RunnerTests+CommandExecution.swift:2007-2012) andexecuteAcceptedstamps whatever the switch returned (:928-933,response.stampingTargetActivation(fact)), so the fact is on the wire. It dies one level up, at the projection this issue's item 1 does not name:packages/platform-apple/src/interactor.ts:78-86casts the response to{ found?: boolean }and returns{ found: result?.found === true }, so everything the same response carried is discarded before the daemon ever sees it. From theresrc/commands/interaction/runtime/wait-text.ts:42-43only ever gets a boolean, and a satisfiedwait textanswers with no disclosure at all. A timed-outwait textdoes disclose when its surface-decoration capture re-activated — that path was covered in #2693 — which makes the satisfied case look intentional unless you read the projection.So item 1 ("decode the stamped fact where runner responses are read for every command") has to survive two projections, not one: the interaction projection named here, and this text-read projection whose return type has no room for a fact. I'd suggest the acceptance list name it explicitly:
- A text-read command answered without a capture —
wait textsatisfied on the first poll after an off-app handoff — carriestargetActivationand the appended warning, or has an explicit reason why a text read is not a repair bill.
Same non-goal holds:
otherActiveApplicationPidstays a liveness claim. Flagging rather than folding it into #2693, which is open as it stands and would need a second projection change beyond its stated scope.- A text-read command answered without a capture —
Split out of #2682 / PR #2693 by adversarial review.
What is already shipped
The Apple runner stamps
targetActivationon the response of every accepted command that had to re-activate the session app (RunnerTests+CommandExecution.swift,executeAccepted). PR #2691 decodes it for captures; PR #2693 discloses it on the daemon routes that consumed a capture:snapshot,find,get,is,wait, and an interaction whose target tree was captured for it.The gap
A command that consumes no capture —
press <x> <y>, orpress @refanswered from a live ref frame — still answers with no disclosure even when the runner paid for a foreground repair to serve it. The stamp is on that command's runner response and is dropped at the Apple interaction projection: onlypackages/platform-apple/src/runner/snapshot-presentation.tsdecodes it today.This is the ADR 0012 category: a repair happened and the response does not say so. Silence must not be read as "no repair".
website/docs/docs/commands.md("Foreground repairs on iOS") states that boundary until this closes.Shape of the fix
packages/platform-apple/src/runner/runner-session.ts(the sharedparseRunnerResponseseam that already reads therunnerMainThreadBusystamp) plus whatever per-command projection needs to let it through.buildInteractionResponseData(the shared interaction response builder) so no interaction verb can drop it.test/integration/smoke-ios-target-activation.test.ts(now wired intoios.ymlandreplays-manual.yml), which asserts the disclosure for capture-consuming commands today.Acceptance
press <x> <y>andpress @refafter an off-app handoff each carrytargetActivationand the appended warning for the repair they paid for.Not in scope
Foreground ownership:
otherActiveApplicationPidremains a liveness claim;activeApplicationsexposes no ordering, so no field here proves which app owned the screen.