Repository navigation
fix(ios): alert dismiss picks a listed decline, never a guessed one - #3715
Conversation
There was a problem hiding this comment.
All reported issues were addressed across 13 files
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
|
Size Report
Startup median (7 runs, lower is better):
|
38d737f to
2ab0df6
Compare
|
The code change looks right at 2ab0df6, but the device-facing route has no live evidence yet. I need that before this can merge. The only on-device evidence is the CI XCTests at c47fcd5. They call Not blocking, and fine to take or leave: add one Unreleased line to CHANGELOG.md for the dismiss refusal and the new code, since a failed Of the open inline threads, the two from cubic-dev-ai no longer apply, so please resolve them: RunnerAlertButtonChoice.swift comment is fixed because the header now explains why a positional guess is unsafe, and alert.ts comment is fixed because the help text now names the "dismiss popup" marker and the accept refusal. Smoke Tests is still queued, not failed, so CI has not given a result yet. No conflicts are known. I read the code and did not run the XCTests or the CLI. The iOS lane result and the affected-check receipt are quoted from the author. Before merge, the live ATT run with auth_value 0 and the CLI refusal showing ALERT_BUTTON_NOT_FOUND with |
|
Live simulator evidence at this head ( Device: iPhone 17e simulator, iOS 27. Runner built from this checkout into a lane-owned derived path. Real system ATT prompt → decline tapped, iOS recorded a denial Precondition without any global reset: I installed a disposable canary app under its own bundle id with Runner activation, one tap only: Ground truth after that tap — the canary simulator's TCC.db, own bundle only: and the app's own callback logged Refusal reaches the CLI with no dispatch The runner's own fixture, launched through the CLI:
The runner hint survives One honest note: I left the CHANGELOG line and the macOS helper follow-up alone as non-blocking, and the two cubic-dev-ai threads are already resolved. Gate receipt and isolated review remain those for |
|
Thanks, this live run at 2ab0df6 covers what I asked for. On the real ATT prompt, This is ready for human review. Echoing the tapped label in the |
…ative one chooseAlertButton answered 'dismiss' with the last button whenever no known decline label matched. The App Tracking Transparency prompt lists 'Allow' last and words its decline in a label the list never carried, so 'alert dismiss' reported 'dismissed' while iOS recorded an authorization (#3627). Move the accept/dismiss choice into RunnerAlertButtonChoice so the rule is unit-testable without a screen, and make dismiss answer only a button the accept rule does not claim: the known decline label when present, otherwise the last button no accept label matches, which covers declines worded in a language or phrasing the list does not carry. A prompt whose every button reads affirmative now fails with 'alert dismiss button not found' and taps nothing. Recognize 'Ask App Not to Track' as a decline label. The runner fixture app gains an ATT-shaped prompt with an answer canary so the iOS lane asserts which button the app really took.
Cold-review follow-ups on the decline-choice rule: - The new 'dismiss has no button to name' refusal now answers with the runner code ALERT_BUTTON_NOT_FOUND and a hint, bound in RUNNER_ERROR_CODE_DISPATCH as dispatched: no like every other reply that ran nothing, with a row and driver in the dispatch-disclosure golden table. Before, the codeless refusal read as dispatched: unknown and taught callers to key on the message text. - The decline-choice fixture carries the one-shot guard its sibling uses and an affirmative-only variant, so the refusal's response shape is asserted end to end: code ALERT_BUTTON_NOT_FOUND, canary Unanswered. - The alert command description and the commands.md note state the refusal by its typed code, name the one-button 'OK' case, and stop promising that the fallback finds a decline.
Cold-review follow-up on the decline-choice rule: - iOS localizes the affirmative button too, so a fully localized prompt leaves every label unclaimed and the last-unclaimed fallback answered the localized 'Allow' — the same authorize-on-guess failure #3627 is about, one locale over. The fallback now fires only when exactly one button is unclaimed; when more than one is, the choice refuses with ALERT_BUTTON_NOT_FOUND and taps nothing, like an all-affirmative prompt. The localization unit vector carries both buttons in the locale, not a German decline paired with an English 'Allow'. - 'ask app not to track' in the decline list is pinned by the shape only it decides: ['Ask App Not to Track', 'Learn More']. - The no-button-activated hint is named once and shared by the refusal and the unactivated verification branches so the two cannot drift. - The command description and commands.md state the scoped guarantee and the runner boundary instead of promising any unclaimed label is a decline, and the two behavior pins are annotated as pins.
Second cold-review round on the decline-choice rule: - 'Accept', 'I Agree', 'Got it', 'Allow Tracking' and 'Use App' were outside isAcceptButton, so the sole-unclaimed fallback activated a one-button consent prompt of that copy and reported 'dismissed' — the incident's shape one label away from the list. The accept list now claims them, with pins. - The two canary fixtures resolve under the sibling lane's resolution allowance instead of the 10s command timeout: resolution is a pure read that a contended hosted simulator has spent 40s on, and timing it out would read as a red fix. - commands.md and the CLI description state the guarantee as the runner's English label lists actually deliver, attribute the ATT decline to its list entry, and stop promising 'every button reads affirmative' beyond what the lists recognize. - Pin comments state the invariant they hold rather than this PR's review history.
Parent-evidenced vectors showed the sole-unclaimed fallback still reached consent copy the accept list had not been taught yet: 'Accept all', 'Agree to Terms', 'I accept', 'Accept terms' were each tapped and reported 'dismissed'. Growing the accept list cannot close an open-ended copy space, so the rule stops guessing: - dismiss resolves only a label isDismissButton recognizes; any other prompt — unknown wording, localization, consent copy — answers ALERT_BUTTON_NOT_FOUND and taps nothing, the issue's stated alternative to picking the decline. Unknown buttons stay reachable by name through 'alert get' plus snapshot + press. - With no fallback, the accept list needs no consent-label arms race and returns to its base membership. - The pure tests pin the refusal set with the parent's exact shapes, keep the known-decline positives including the 'Ask App Not to Track' entry pin, and keep accept's guards unchanged. - commands.md, the CLI description, and the code contract constant's doc say the list-scoped rule and name the press-by-label path.
…ts marker
alert dismiss on an app-owned popover resolved through the native
"dismiss popup" marker regressed: that surface's close control ("Close
popover") is on no decline list, and base reached it through the
positional fallback. Restore it as a property of the surface, not a
position: only a RunnerAlert whose source is the marker window may take
its last actionable element, so consent prompts (never marker-marked)
keep the #3627 refusal. Pin the surface through chooseAlertButton and
the label rule in the pure table.
Also from review: scope the decline-list claim to the Apple XCUITest
runner in the CLI description and wire-code contract, declare
ALERT_BUTTON_NOT_FOUND in RunnerWireErrorCode where the protocol names
wire codes, and drop the migration narration from the user doc.
…arration out of the alert fixtures
2ab0df6 to
9203a7a
Compare
Summary
Closes #3627. On the Apple XCUITest runner,
alert dismisschooses a recognized decline action, including "Ask App Not to Track", or refuses withALERT_BUTTON_NOT_FOUNDanddetails.dispatched: no. Unknown and localized choices receive no guessed tap. The exact "dismiss popup" marker retains its close-element behavior; accept-side vocabulary is unchanged.Validation
Published head:
9203a7a4066fec99f54ef32710350bfa6462f9d7, rebased ontof633525b1, mergeable.2ab0df6d0, so the live proof below transfers to this commit by construction. Conflicts retain both upstream text-input refusal codes and this branch's alert refusal code. A fresh isolated full-access Apex review in its own checkout at9203a7a40: approve, no blocking findings; it independently verified union fidelity, the frozen decline-only/accept vocabularies, the exact-marker exception, and contract coherence after the upstream reason-table refactor.run-LlH55Nis queued behind live repository validation; no passing receipt is claimed yet. The owner runs the 11 alert XCTests on-device after the broad gate; that receipt will be appended here.2ab0df6d0: one decline activation, TCC denied and the disposable app's denied callback. The affirmative-only fixture refused through CLI/daemon/runner withdispatched: no, preserved its hint and logged zero activations. No global privacy reset was used.The readiness label remains present, but the exact-head gate, native tests and current-head CI are pending. The earlier head's green CI and gate do not certify this rebased commit.