Summary
fill verifies text entry by exact string comparison against element.value. Any field that normalizes what it stores — digit grouping, masks, currency formatting — fails that comparison even when entry succeeded. fill then runs its destructive repair (clear + retype) against an already-correct field, and finally returns TEXT_ENTRY_MISMATCH.
Observed
iOS 26.6.1, physical iPhone SE (3rd generation), agent-device 0.20.5. The relevant logic is unchanged on main. Target: a Flutter text field applying a ## ### ## ## digit-grouping formatter.
$ agent-device fill 187 286 '000629177'
Error (TEXT_ENTRY_MISMATCH): text entry verification failed:
expected "000629177", observed "00 062 91 77"
00 062 91 77 is exactly the requested digits, formatted. The field held the correct value both before and after the repair. From the runner log, emitted before repair ran:
AGENT_DEVICE_RUNNER_REPAIR_TEXT_ENTRY expectedLength=9 observedLength=12
observedLength=12 is the complete, correctly formatted value.
Entry itself is not the problem. agent-device type '000629188' --delay-ms 0 into the same empty field produces the correct value on the first attempt, so there is no dropped-character or timing issue to repair.
Cause
In apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift on main:
textEntryValueMatchesExpected (~line 510) accepts only exact equality, plus an exception for a trailing submit suffix. Any normalization fails.
isRepairableTextEntryMismatch (~line 601) returns true unconditionally when repairMode == .replacement, so it never consults isLikelyDroppedCharacterTextEntryMismatch immediately below it. That subsequence check would already classify this mismatch as non-repairable.
So replacement-mode entry into any normalizing field always follows the same path: mismatch, repair, mismatch, error.
Impact
The repair is the more serious half. clearTextInput sends moveCaretToEnd — an edge tap computed from a frame the code's own comment notes may be stale — plus up to 120 delete keystrokes, then retypes. It mutates a field that was already correct, and the retype has no first-character warmup, so it is strictly less reliable than the entry it replaces.
There is no way to opt out. verified == false is a hard error in RunnerTests+CommandExecution.swift, and fill exposes no --no-verify or --expect flag.
Required behavior
fill succeeds when the field's value is a normalization of the requested text — at minimum, when every requested character appears in order in the observed value.
- Replacement-mode repair does not run when the observed value already contains the requested characters in order.
- Optionally, a way to state the expected post-normalization value:
agent-device fill <target> '000629177' --expect '00 062 91 77'
Completion conditions
fill into a field that groups digits as they are typed returns ok and performs no clear/retype.
- A field that genuinely drops characters still mismatches and still repairs.
- Regression fixture:
examples/test-app's FormScreen already has a field-phone input, which currently stores the raw value. Formatting it on change reproduces this, and fill 'id=field-phone' '000629177' should then pass.
Related
Summary
fillverifies text entry by exact string comparison againstelement.value. Any field that normalizes what it stores — digit grouping, masks, currency formatting — fails that comparison even when entry succeeded.fillthen runs its destructive repair (clear + retype) against an already-correct field, and finally returnsTEXT_ENTRY_MISMATCH.Observed
iOS 26.6.1, physical iPhone SE (3rd generation), agent-device 0.20.5. The relevant logic is unchanged on
main. Target: a Flutter text field applying a## ### ## ##digit-grouping formatter.00 062 91 77is exactly the requested digits, formatted. The field held the correct value both before and after the repair. From the runner log, emitted before repair ran:observedLength=12is the complete, correctly formatted value.Entry itself is not the problem.
agent-device type '000629188' --delay-ms 0into the same empty field produces the correct value on the first attempt, so there is no dropped-character or timing issue to repair.Cause
In
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swiftonmain:textEntryValueMatchesExpected(~line 510) accepts only exact equality, plus an exception for a trailing submit suffix. Any normalization fails.isRepairableTextEntryMismatch(~line 601) returnstrueunconditionally whenrepairMode == .replacement, so it never consultsisLikelyDroppedCharacterTextEntryMismatchimmediately below it. That subsequence check would already classify this mismatch as non-repairable.So replacement-mode entry into any normalizing field always follows the same path: mismatch, repair, mismatch, error.
Impact
The repair is the more serious half.
clearTextInputsendsmoveCaretToEnd— an edge tap computed from a frame the code's own comment notes may be stale — plus up to 120 delete keystrokes, then retypes. It mutates a field that was already correct, and the retype has no first-character warmup, so it is strictly less reliable than the entry it replaces.There is no way to opt out.
verified == falseis a hard error inRunnerTests+CommandExecution.swift, andfillexposes no--no-verifyor--expectflag.Required behavior
fillsucceeds when the field's value is a normalization of the requested text — at minimum, when every requested character appears in order in the observed value.Completion conditions
fillinto a field that groups digits as they are typed returns ok and performs no clear/retype.examples/test-app's FormScreen already has afield-phoneinput, which currently stores the raw value. Formatting it on change reproduces this, andfill 'id=field-phone' '000629177'should then pass.Related
typecannot verify on iOS at all.focusedTextInputreturns nil by design there, so baretyperesolves no target element,expectedTextcomes out nil, andverifyTextEntryreturnsverified: nil. This makestypeappear more reliable thanfillwhen it is only unchecked. Probably worth tracking separately.