Repository navigation
Conversation
On a non-English iOS simulator, the `Open in "<App>"?` confirmation that `open --launch-url` raises stayed unrecognized (`alert-unrecognized`), and `alert accept` refused its button (`Ouvrir`) with ALERT_BUTTON_NOT_FOUND. The runner now reads SpringBoard's own SchemeApproval strings (CoreServices.framework) with the strings it already reads for permission prompts. Their open and cancel labels join the alert vocabulary, so `alert accept` and `dismiss` answer the prompt in each preferred language. The runner also marks an alert read as the launch confirmation when its title and both buttons are the OS's wording of one language, and `open` accepts a marked prompt instead of matching English text. Unreadable strings recognize nothing, as before. Fixes #3922 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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):
|
Apex SentinelVerdict: Since the last review: 4 new · 4 still open · 4 no longer raised 8 finding(s): 3 minor, 5 nit. Verdict: approve. 🟡 Minor (3)
🔵 Nit (5)
Reply under an inline comment with Earlier reviewsReview of
|
| Where | Finding |
|---|---|
packages/platform-apple/src/launch-confirmation.ts:191 |
Launch-confirmation recognition now depends entirely on a runner-side field: older or non-updated runners silently stop answering even the English prompt |
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerLaunchConfirmationPrompt.swift:56 |
🆕 Localized launch confirmations are recognized only when the Simulator's languages match SchemeApproval's localization names exactly |
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerLaunchConfirmationPrompt.swift:62 |
🆕 RunnerSystemPromptStrings.read now drops the whole launch-confirmation wording when any one preferred language lacks SchemeApproval.strings |
🔵 Nit (5)
| Where | Finding |
|---|---|
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAlertLabelVocabulary.swift:41 |
SchemeApproval is read twice and the fallback path drops the launch-confirmation log |
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAlertLabelVocabulary.swift:49 |
🆕 composed assumes the whole SchemeApproval table contributes only Open/Cancel labels; that invariant is nowhere enforced |
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerLaunchConfirmationPrompt.swift:21 |
🆕 try! on a regex literal in a new file |
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSystemPromptStrings.swift:58 |
Runner log marker renamed, and the new subsystems are inline literals |
packages/platform-apple/src/launch-confirmation.fixtures.ts:5 |
🆕 Marking the shared CONFIRMATION fixture inverts negative cases that spread it |
Review of 232f6fc6 · time not recorded
Apex Sentinel
Verdict: request_changes
8 finding(s): 1 major, 4 minor, 3 nit. Verdict: request_changes. reached max rounds (1).
Required fixes
- Adding SchemeApproval to promptTables makes the whole localized alert vocabulary depend on a table only the launch confirmation needs
🟠 Major (1)
| Where | Finding |
|---|---|
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSystemPromptStrings.swift:10 |
Adding SchemeApproval to promptTables makes the whole localized alert vocabulary depend on a table only the launch confirmation needs |
🟡 Minor (4)
| Where | Finding |
|---|---|
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerLaunchConfirmationPrompt.swift:19 |
A title format without %@ silently matches an empty app name |
packages/platform-apple/src/launch-confirmation.ts:185 |
Host-side English recognition removed: an open against a runner built before this PR no longer recognizes any launch confirmation, English included |
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift:140 |
alert get now returns a new undocumented launchConfirmation: true field |
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift:448 |
launchConfirmation is now a boolean in alert get data and a string enum in open data |
🔵 Nit (3)
| Where | Finding |
|---|---|
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSystemPromptStrings.swift:57 |
One log marker now covers two different failures |
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerLaunchConfirmationPrompt.swift:14 |
Duplicated empty recognizer: unavailable and the non-simulator system |
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift:448 |
New runner payload field reaches alert get undocumented |
There was a problem hiding this comment.
Apex Sentinel: 8 new findings on 232f6fc. The verdict and the full list are in the summary comment.
|
Reviewed 232f6fc. The fix is incomplete: the Finnish launch confirmation is still not recognized. CI is pending, because Smoke Tests is still running. No conflicts. Before merge, the matcher needs the fix below and the pending RunnerLaunchConfirmationPrompt.swift:19 escapes the OS title format, then replaces only the literal token Not blocking, take or leave: once the Finnish case is fixed, the On the other threads: the cubic P3 thread on the duplicated Evidence limits: the French live run is author-reported and I could not reproduce it read-only. I checked SchemeApproval formats only on the installed iOS 27.0 and 27.1 runtimes. I did not run the claim that the Swift vocabulary test fails without the table. |
…ion prompts A runtime without the SchemeApproval table no longer drops the notification, tracking, and location vocabulary. The test shares the CONFIRMATION fixture. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Apex Sentinel could not review commit Apex Sentinel could not read or check out this pull request, which is usually temporary (GitHub was slow, or the branch moved while the review waited). If it keeps failing, tell whoever runs Sentinel for this repository, or check that the App still has access to it. Push a new commit or comment |
Rename the runner's alert mark to launchConfirmationPrompt so it cannot be read as open's launchConfirmation outcome, and document it. Accept positional app-name placeholders in the OS title format, skip a wording with no placeholder, and name the failing subsystem in the unavailable-strings log. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
All reported issues were addressed across 10 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Thanks for the update. At 36b85c9 the slot-less wording guard is in place, but the Finnish problem from the earlier review (#4160 (comment)) is still there, and the regression test I asked for is still missing. The rename to The slot regex at https://github.com/callstack/agent-device/blob/36b85c9/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerLaunchConfirmationPrompt.swift#L21 is The test at https://github.com/callstack/agent-device/blob/36b85c9/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/UnitTests/RunnerTests+LaunchConfirmationPromptTests.swift#L67 covers only The cubic-dev-ai thread on the unscoped Not blocking, and fine to take or leave: the PR body still names the alert field There are no conflicts. Smoke Tests (apple.yml, macos-26) is still in progress. It builds and drives the iOS XCUITest runner, and this diff changes runner Swift sources and the launch-confirmation answer, so a failure there would likely be related until shown otherwise. I checked the SchemeApproval formats only on the installed iOS 27.0 and 27.1 simulator runtimes. I did not run the Swift unit tests or the runner build, so the claim that the tests pass at the head comes from reading them. I did not reproduce the French live run. It is author-reported, and it predates the field rename and the split table read. Before merge, please match every object specifier such as This review covers 36b85c9. The newer head 81577da changes only that commands.md line, so the Finnish matcher and test points above still apply. |
The launch-confirmation mark now needs the SpringBoard blocking-modal source, so an app alert with the same wording and buttons is not auto-accepted by open. The title placeholder accepts Apple's bracketed object format (Finnish `%[tt]@`). Tests load every installed SchemeApproval localization, cover the missing-table branch, and cover the app-owned alert. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Apex Sentinel: 5 new findings on f862d9e. The verdict and the full list are in the summary comment.
…table Another key of that undocumented table cannot join the accept or dismiss labels. The placeholder regex no longer force-unwraps. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Apex Sentinel: 4 new findings on 9435060. The verdict and the full list are in the summary comment.
| } | ||
| } | ||
|
|
||
| let title = preferredAlertTitle(alert.root, buttons: alert.buttons) |
There was a problem hiding this comment.
🆕 🟡 Minor · Only the handleAlert read branch marks the prompt; the awaitAlert/probe read paths publish no mark
launchConfirmationPrompt: true is added in exactly one place in the runner: the non-action return of handleAlert (RunnerTests+Alert.swift:140-149). Every other alert-shaped payload was left untouched — blockingSystemModalPayload (RunnerTests+SystemModal.swift:96-103), the system-modal probe payloads (RunnerTests+Models.swift:394/404/414) and the accept/dismiss verification payload (RunnerTests+AlertObservation.swift:81) publish no launchConfirmationPrompt at all. On the host side, createLaunchConfirmationPort uses awaitAlert for the first read that decides whether the prompt is recognized and readAlert again in verifyFailedAcceptance; those legs are what decide accepted vs alert-unrecognized. If the alert legs for the launch-confirmation probe are served by the system-modal probe/wait path rather than by the handleAlert read branch, the mark never crosses the wire, isLaunchConfirmation is always false, and the whole open --launch-url answer keeps failing with alert-unrecognized — while the newly documented alert get output would still look right, so the live check in the PR description would not catch it. I could not finish verifying which runner command serves the awaitAlert leg inside my read budget, so this is reported at low confidence as the invariant the change silently rests on: every host-visible alert read that the confirmation answer performs must go through the one branch that computes the mark.
Suggested change
Verify the runner command behind the interactor's awaitAlert/readAlert legs is the handleAlert read branch that now sets launchConfirmationPrompt; if any of those legs resolves through the blocking-system-modal probe payload, mark it there too (or move the mark into the shared payload builder) so open sees the same flag alert get prints.
Details
persona: javascript
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if this is wrong — I will re-check the cited code.
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
| (path: "/System/Library/Frameworks/CoreLocation.framework", table: "locationd") | ||
| ] | ||
|
|
||
| /// SpringBoard's launch confirmation. It is read apart from `promptTables`, so a runtime without |
There was a problem hiding this comment.
🆕 🔵 Nit · /// The only keys … comment sits under the promptTables doc block on launchConfirmationLabelKeys
RunnerSystemPromptStrings.swift:18-21 puts two unrelated documents on one declaration: the first sentence describes launchConfirmationTable (read apart from promptTables), which is declared 6 lines later, and the second describes launchConfirmationLabelKeys, the declaration it is attached to. RunnerLaunchConfirmationPrompt.fromSystemStrings uses the full unfiltered table, and RunnerAlertLabelVocabulary.composed is the only consumer of launchConfirmationLabelKeys, so the misplaced sentence points a reader at a restriction that the other consumer does not apply. Split the two sentences onto their own declarations.
Suggested change
Keep the "read apart from promptTables" sentence with launchConfirmationTable and leave only the key-restriction sentence on launchConfirmationLabelKeys.
Details
Also raised here
Also raised at this location by swift-ios: Doc comment for launchConfirmationLabelKeys mixes two instructions into one block — The comment on launchConfirmationLabelKeys (lines 18-22) explains two unrelated things: why SchemeApproval is read apart from promptTables (which belongs to launchConfirmationTable, declared five lines below with no comment) and why only two keys are taken. AGENTS.md asks for one instruction per sentence and comments attached to what they describe; splitting these makes the key restriction, which is the security-relevant constraint, easy to miss.
persona: javascript, maintainability, swift-ios
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if this is wrong — I will re-check the cited code.
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
| path: "/System/Library/Frameworks/CoreServices.framework", | ||
| table: "SchemeApproval" | ||
| ) | ||
|
|
There was a problem hiding this comment.
🆕 🔵 Nit · read takes a table list but throws away every table that did read on the first failure
RunnerSystemPromptStrings.read accumulates into read and returns .failure from inside the loop, so a caller that passes more than one path gets no partial result — the tables that read are discarded when a later one does not. promptTables relies on exactly that all-or-none rule, but launchConfirmationTable is passed in its own call only because a shared multi-table call would lose the tables that read. Either state that all-or-none contract in the doc comment above read (it currently only says "A table reads in every preferred language or not at all") or drop the array parameter so the shape cannot invite the wrong call.
Suggested change
Document that read fails as a whole when any path fails, or take a single (path, table) so the partial-read trap cannot be reached.
Details
persona: maintainability
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if this is wrong — I will re-check the cited code.
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
| } | ||
| } | ||
|
|
||
| let title = preferredAlertTitle(alert.root, buttons: alert.buttons) |
There was a problem hiding this comment.
🆕 🔵 Nit · The launch-confirmation matcher is initialized lazily on the alert-read path and can be built twice
RunnerLaunchConfirmationPrompt.system is a lazy static let that parses SchemeApproval.strings for the OS root in English plus every preferred language (RunnerSystemPromptStrings.read), and it is now consulted from marksLaunchConfirmation on every alert get (RunnerTests+Alert.swift:147, 154-158). The only warm-up is the DispatchQueue.global(qos: .utility).async block in setUp, which builds the vocabulary first and this matcher second, so it serializes two table walks. alert get and alert wait have no activation gate before that lazy read (the hittable probe in probeAlertButtonHittable is accept/dismiss only), so on a host that has not finished the warm-up — a stalled utility queue on a loaded machine, or any alert get racing the prefetch — the first alert read pays the whole file read synchronously inside its own deadline (default 10 s, and LAUNCH_CONFIRMATION_PRESENTATION_TIMEOUT_MS is only 3 s on the launch path) and can report a timeout for a prompt that is plainly on screen. The prefetch comment claims the deadline is protected; it is protected only if the utility block wins the race. Also note RunnerAlertLabelVocabulary.fromSystemStrings reads the same SchemeApproval table a second time, so the warm-up cost is doubled.
Suggested change
Warm both statics concurrently, or share one read of the SchemeApproval table between the vocabulary and the prompt matcher, so no alert read can be the first reader.
Details
persona: senior-developer
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if this is wrong — I will re-check the cited code.
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
Summary
Fixes #3922. On a non-English iOS simulator, the
Open in "<App>"?confirmation raised byopen --launch-urlstayedalert-unrecognized, andalert acceptrefusedOuvrirwithALERT_BUTTON_NOT_FOUND. This extends #4006's mechanism to that prompt. No new flag or command.CoreServices.framework, tableSchemeApproval. The runner already reads that framework's simulated-OS root, so the table is readable. It holds the title format (SCHEME_APPROVAL_PROMPT_TITLE_NO_SOURCE) and the open and cancel labels.OuvrirandAnnulernow translateOpenandCancelin every preferred language, soalert acceptandalert dismissanswer the prompt. Matching stays exact and fix(ios): alert dismiss picks a listed decline, never a guessed one #3715's rule holds: no guessed label.RunnerLaunchConfirmationPromptrecognizes the prompt from the OS's own title format and both buttons of one language. The runner setslaunchConfirmationPrompt: trueon that alert read, andopenaccepts a marked prompt. This replaces the TypeScript English title and button match. Unreadable strings recognize nothing, so the prompt staysalert-unrecognized.RunnerSystemPromptStringsreader serves both consumers.Behavior to know:
alert getnow also printslaunchConfirmationPrompt: truefor this prompt, because the runner payload passes through. It is documented in the alert section ofcommands.md. Say so if you want it stripped.Tests
UnitTests/RunnerTests+LaunchConfirmationPromptTests.swift), run on the iPhone 17e simulator:fr-FRandja-JPrecognize the prompt, andacceptpicksOuvrirand開くwhiledismisspicksAnnuler; German is not recognized, because it is not a preferred language;acceptreturnsnilfor["Annuler", "Ouvrir"].Live check (iPhone 17e, F25C7515, iOS 27.0)
French set per simulator (
AppleLanguages ("fr"),AppleLocale fr_FR) and rebooted, with a copy of the test app that has a fresh URL scheme (the lab app's schemes were already approved):open <app> --launch-url adt3922fr://x --relaunchreturnedlaunchConfirmation: "accepted".xcrun simctl openurlraising the prompt again,alert getreturnedmessage: "Ouvrir dans « Agent Device Tester » ?",items: ["Ouvrir", "Annuler"],launchConfirmationPrompt: true.alert acceptreturnedaccepted, and the nextalert getfound no alert.en-US, pl-US, es-PL, zh-Hans-PL, fi-PLanden_US@rg=plzzzz, the test copy was uninstalled, the session was closed, and the simulator was shut down.iOS and macOS runner builds pass.
check:xctest-selectionandcheck:packaged-runner-swiftpass.Validation
pnpm check:affected --run --head 9435060405(exactCommit=true,run-wZWELW): every stage passed, includingunit,xctest-selection, andpackaged-runner-swift. Earlier runs failed only on host-load timeouts (load average above 200) in unrelated tests, which pass alone.Review round 3 (Finnish
%[tt]@, SpringBoard-only mark, missing-table test) is covered by new simulator tests: one loads every installedSchemeApprovallocalization and checks each rendered title is recognized, one covers the missing-table branch, and one checks an app-owned alert with identical text is not marked. Before the fix, the Finnish, every-localization, and app-owned cases fail.Live on the iPhone 17e with fresh unapproved schemes, French and Finnish:
open --launch-urlreturnedlaunchConfirmation: "accepted",alert getreturnedlaunchConfirmationPrompt: true(Ouvrir dans « Agent Device Tester » ?withOuvrir/Annuler;Avataanko Agent Device Tester-apissa?withAvaa/Kumoa), andalert acceptanswered it. The language list was restored afterwards.CI owns the rest.
Risks: the string-table location is undocumented, as in #4006. A runner older than this change does not set
launchConfirmation, soopenreports its prompt as unrecognized until the runner is rebuilt.🤖 Generated with Claude Code