Purpose
#3356 settles the next screen read after an iOS open that returns timing.postOpenObservation: "unobservable". Three cases from #3354 are still open. In each one a selector click can tap a stale point and report success:
- The layout moves between the click's own pre-tap capture and its tap, at any time and not only after an open.
- The layout moves after an
observable open. observable means the tree was readable, not that it was at rest.
- The post-open read never settles. The click gets
postGestureOutcome: unsettled and a warning, then taps anyway.
Evidence
The evidence is the same as #3354: main run 37815426076, where General was at y=319 and the settled list had it at y=406. Nobody has reproduced the shift locally. In 5 live runs of 01-settings.ad on an iOS 27.0 simulator, every read settled before the tap. Collect evidence that these cases happen before choosing a design.
Required behavior
- When the target's position can't be confirmed at dispatch, the click fails with a typed reason instead of tapping the old point. Declare the reason and its
details shape in the error taxonomy.
- Judging that "the target moved" requires the target's identity across reads. The whole-surface signature comparison (
src/daemon/interaction-surface-signature.ts) can't establish it, and it collapses duplicate identities.
- Do not add a capture or runner query to warm clicks whose layout is already stable.
- Give any bounded wait a wall-clock deadline, not only a polling budget. Capture cancellation must propagate: abort the capture and wait for it to stop, rather than racing a timer. Today the post-open loop's 1.5 s is a polling budget, and its two reads took 2.55–2.57 s live.
- A screen that animates constantly must not make a stable target unclickable.
- Update
TAP_OUTCOME_NOT_OBSERVED_GAP in packages/contracts/src/interaction-guarantees.ts.
Completion
- A fixture where the target moves between the click's capture and its tap. The click either taps the new location or fails with the typed reason.
- Warm-click p50 on
01-settings.ad, measured before and after the change, does not regress. It was about 1.3 s on the iOS 27.0 simulator.
Rejected so far
- A runner-side frame check on every click. It would take an estimated 550–800 lines and add a query to every warm click.
- Settling after every iOS open. The first click would cost about 1.8 s more, with no evidence that
observable opens shift, and it still wouldn't cover shifts at other times.
- Failing whenever the settle times out. A stable target next to an unrelated animation would be refused.
Related: #3354, #3356, #3335.
Purpose
#3356 settles the next screen read after an iOS
openthat returnstiming.postOpenObservation: "unobservable". Three cases from #3354 are still open. In each one a selector click can tap a stale point and report success:observableopen.observablemeans the tree was readable, not that it was at rest.postGestureOutcome: unsettledand a warning, then taps anyway.Evidence
The evidence is the same as #3354: main run
37815426076, whereGeneralwas at y=319 and the settled list had it at y=406. Nobody has reproduced the shift locally. In 5 live runs of01-settings.adon an iOS 27.0 simulator, every read settled before the tap. Collect evidence that these cases happen before choosing a design.Required behavior
detailsshape in the error taxonomy.src/daemon/interaction-surface-signature.ts) can't establish it, and it collapses duplicate identities.TAP_OUTCOME_NOT_OBSERVED_GAPinpackages/contracts/src/interaction-guarantees.ts.Completion
01-settings.ad, measured before and after the change, does not regress. It was about 1.3 s on the iOS 27.0 simulator.Rejected so far
observableopens shift, and it still wouldn't cover shifts at other times.Related: #3354, #3356, #3335.