Repository navigation
fill repairs and fails on fields that normalize their input #2634
Description
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) 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. 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
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.- 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
fillinto 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 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.- Limrun iOS: Flutter text entry fails with “No focused accessibility element found” after tapping an editable field #2578 / fix(limrun): witness the field a fill taps before typing #2592 concern a different Flutter symptom (focus acquisition, not verification).
Activity
fix(android): name the placeholder in a fill verification failure (#2985
fix(ios): stop fill from repairing fields that normalize their input (#…
fix(ios): stop fill from failing and repairing fields that normalize …
fix(ios): stop fill from failing and repairing fields that normalize …
fix(ios): stop fill from failing and repairing fields that normalize …
test(ios): pin the completion rule's residual provenance on both sides (
docs+test(ios): pin the template-retaining mask as the completion rul…
fix(ios): observe fill once without destructive repair (#3334)