Blolabel
← Notes
analysis 7 min read

iOS Settings exposes no selected state, so a check that finds the word Never passes whether or not Auto-Lock is Never

Three checks reported success on a phone that was not set up: a selection check that looked for text, a Settings walk that never left its first screen, and an app launch that opened a system service. Each fix replaces a presence check with a witness.

On iOS 26.6 the Auto-Lock page in Settings exposes no selection state in the accessibility tree: no selected trait, no value, no isSelected. Our rig confirmed it had set Auto-Lock to Never by finding the word "Never" on the page, a check that passes whether or not Never is chosen, and the phone kept locking mid-run for four benchmark runs. The only evidence of the chosen row is a Button labelled checkmark inside it, and the tree-condensing step we feed to models was dropping that button as decoration.

Abstract technical illustration of a column of identical grey slabs, one of which carries a small bright notch at its edge that is the only thing distinguishing it

Three checks that passed on a phone that was not set up

All three were found on 8 September 2026 by running the benchmark unattended on a physical iPhone 17 Pro Max, and each is a check that reports success because it looked at the wrong thing. They are recorded in sections 10 and 22 of our findings file and fixed in phoneshell/prep.py and phoneshell/apps.py.

The first is the selection check above. Apple documents a selected accessibility trait ("the accessibility element is currently in a selected state") and an isSelected attribute on XCUITest elements. Neither is set on the Auto-Lock rows. The chosen row differs from the others in exactly one way: it has a child Button whose label is checkmark.

The second is the walk to that page. A tap on a Settings row does not always take. When it did not, the walk carried on searching the screen it never left, then reported the child row missing: "could not find Auto-Lock in Settings", on a phone where Auto-Lock is one swipe below Display & Brightness. The error names the wrong failure, and the silent version of it is the one that let the phone lock during a run.

The third happens before any of that. open_app("Contacts") resolved to com.apple.PeopleViewService, a system service that shares the display name, instead of the Contacts app at com.apple.MobileAddressBook. The task failed with the model having done nothing wrong. We now pin 41 spoken names to bundle ids; the map was first checked against the phone's own app catalogue on 7 September 2026, and Contacts was added after this failure.

Auto-Lock, as the tree sees it: one extra child is the whole answer Cell selected trait value isSelected children 30 Seconds none none none StaticText 1 Minute ... 5 Minutes none none none StaticText Never (chosen) none none none StaticText + Button "checkmark" check v1: is "Never" on the page? always yes, chosen or not passed every time; phone kept locking cost: 4 benchmark runs check v2: checkmark inside the row? Button "checkmark" whose centre y falls within the Never cell's frame read from the RAW tree, polled up to 12 s The condensed tree the model sees drops the checkmark button as decoration, so neither a model nor a check built on that tree can tell which row is chosen. iPhone 17 Pro Max, iOS 26.6, English locale. phoneshell/prep.py _row_is_ticked.
Rows between 1 Minute and 5 Minutes are collapsed into one line; each is a Cell with the same empty attributes. The chosen row is the only one with a second child.

What each check looked at, and what was true

checkwhat it looked atwhat it reportedwhat was truefixsource
Auto-Lock set to Neverthe text "Never" anywhere on the pagesuccess, every timethe text is listed whether or not it is chosen; the phone locked mid-runrequire a checkmark button inside the Never row, from the raw treeFINDINGS.md s.22; prep.py
Row selection attributesselected trait, value, isSelectednothing on any rowiOS exposes no selection state on this pagenone available; the checkmark is the evidenceFINDINGS.md s.22; Apple docs
Walk to Display & Brightness > Auto-Lockwhether the next label was on screen"could not find Auto-Lock in Settings"the tap on the parent row never opened iteach hop must change the screen signature, 3 attempts, 3 s timeout eachprep.py choose_setting
Tap after scroll_to_textcoordinates read while the list was movinga tap on the targetthe tap lands on whichever row slid into that spottake a stable snapshot before tappingprep.py
open_app("Contacts")display-name lookuplaunchedit launched com.apple.PeopleViewService41 curated aliases, Contacts pinned to com.apple.MobileAddressBookFINDINGS.md s.10; apps.py
Input reaches the screena full-width swipe, both directions0% of pixels movedreads fine, writes silently droppedabort the run if under 1% movesprep.py check_input_reaches_screen

The common shape: each broken check asked whether something was present, and the fix asks whether something changed or is attached to the right parent. Presence is the cheap question, and on iOS it is the one that lies most often.

Setting one system option, done so that it proves itself

Our prep step is written as a general routine, "walk Settings down a path and choose an option", because every system setting a rig depends on has the same shape. Each step carries its own witness.

choose_setting(["Display & Brightness", "Auto-Lock"], "Never") 1. terminate Settings reopens where it was left 2. open pinned bundle id com.apple.Preferences 3. each hop scroll, stable snapshot, tap screen must change in 3 s up to 3 attempts 4. option tap "Never" 5. witness checkmark in row, raw tree, up to 12 s what each step would have reported without its witness 1: a walk that starts on a sub-page and searches the wrong list 2: "Contacts" opening com.apple.PeopleViewService, the same failure class for any duplicated display name 3: "could not find Auto-Lock in Settings" when the tap on Display & Brightness never took 5: "Never" found on the page, success reported, phone locks mid-run (4 runs lost) phoneshell/prep.py, 8 September 2026. Timeouts are the code's defaults.
Five steps, four witnesses. Step 4 has none of its own because step 5 is its witness.

What it means for anyone grading or driving a phone agent

A task checker that confirms a setting by finding the option's text is measuring whether the option exists. If your benchmark has any "set X to Y" task on an iOS single-select list, check how it is graded. The same blind spot applies to the model: in our condensed tree the model could not see which row was chosen either, so an agent asked "is Auto-Lock set to Never?" had no way to answer from the tree. We now keep the checkmark in the raw read used for verification; whether to surface it to the model is a separate decision we have not measured.

Resolve apps by bundle id, never by display name. Several iOS bundles share a display name, and some are services rather than apps. A curated map is tedious and it is the only thing that removed this class of failure for us.

Do not trust a phone-side setting the rig did not set and verify itself. Auto-Lock had been set to Never by hand on this phone and it still locked mid-run. A lock during an unattended run is expensive, because every task after it fails identically, and a reboot to recover from it ends the session (see the note on reboots).

A judgement about where this is heading: Apple's WWDC26 session on App Intents and onscreen awareness (8 June 2026) is a route by which an app can describe its own state to the system in structured form. If Settings ever exposed its options that way, the checkmark heuristic would be unnecessary. Nothing we have seen on iOS 26.6 does so for the Auto-Lock page.

Check it yourself

Open Settings > Display & Brightness > Auto-Lock on a phone running WebDriverAgent, then read the tree. Every attribute the tree has for the rows will be on these lines.

# the raw XML for the page: compare the chosen row to its neighbours
curl -s localhost:8100/source | grep -i -E 'Seconds|Minute|Never|checkmark'

# the same as JSON, cells and the checkmark with their frames
curl -s 'localhost:8100/source?format=json' \
  | jq -r '.. | objects | select(.type=="XCUIElementTypeCell" or .label=="checkmark")
           | [.type, .label, .value, .rect.y, .rect.height] | @tsv'

# change the selection by hand and read again: only the checkmark's y moves

# our routine and its witness
grep -n "def _row_is_ticked" -A 24 phoneshell/prep.py
grep -n "def choose_setting" -A 40 phoneshell/prep.py
grep -n "PeopleViewService" -B 4 -A 4 phoneshell/apps.py
sed -n '/^## 10\./,/^---/p' FINDINGS.md

What would prove this wrong

Sources

  1. phoneshell, FINDINGS.md sections 10 and 22, 8 September 2026. https://github.com/Blomega/phoneshell/blob/main/FINDINGS.md
  2. phoneshell, phoneshell/prep.py: choose_setting, _row_is_ticked, check_input_reaches_screen. https://github.com/Blomega/phoneshell/blob/main/phoneshell/prep.py
  3. phoneshell, phoneshell/apps.py: COMMON_ALIASES (41 entries). https://github.com/Blomega/phoneshell/blob/main/phoneshell/apps.py
  4. Apple Developer Documentation, UIAccessibilityTraits selected. https://developer.apple.com/documentation/uikit/uiaccessibilitytraits/selected
  5. Apple Developer Documentation, XCUIElementAttributes isSelected. https://developer.apple.com/documentation/xcuiautomation/xcuielementattributes/isselected
  6. Matthew Cassinelli, WWDC26 sessions on App Intents, Siri and AI (App Intents onscreen awareness session, 8 June 2026). https://matthewcassinelli.com/wwdc26-sessions-app-intents-siri-ai-apple-developer