01Field map
The ICA form sits behind an agent login, so I could not write its layout in advance. I split one recorded iMacros script in two: the real element ids, dropdown codes and date formats became a reusable field map, while I threw away every value identifying the recorded traveller and bound it to the dashboard field that supplies it. I committed that map, so a dashboard applicant now fills with no import at all, and importing a further recording still adds that person as an applicant.
02Adjectives, not countries
Matching dropdown options on their visible text worked for Russia and quietly failed for Ukraine, Moldova, Kyrgyzstan and Turkmenistan, because ICA renders UKRAINIAN, MOLDAVIAN, KYRGYZ REPUBLIC. I read the real option codes off the live form and committed them to the vocabulary file. If ICA changes a list, the fill reports no option with that code rather than picking a neighbour.
03Filling in passes
SAVE is JSF with PrimeFaces and cascading dropdowns: setting Country of Birth fires an ajax response that replaces that part of the page, taking anything already filled there with it, so a single sweep ends with only the last field surviving. The fill runs in passes instead. Dropdowns go first while the text boxes are still empty, then it waits for the page to go quiet, looks again, and refills what got wiped; PrimeFaces selects are not real select elements, so I write both the hidden select and the visible label through the prototype's native setter.
04Reading the passport
I built and threw away two passport-reading designs before this one, which runs in an offscreen document, the only MV3 context with both a DOM and a Worker, and fetches the scan itself so megabytes of pixels never cross a message channel or reach the government page. Most passports on file turned out to be PDFs rather than images, several of them multi-page and some rotated, so every page and orientation is searched and the machine-readable zone's check digits decide which combination was right. Each page is read twice at different scales and a field is only offered when both readings agree; anything ambiguous is left blank with a reason, because a plausible wrong value is a false statement on a government application.
05Identity, not index
Adversarial review of my passport work kept turning up defects of one shape: a path that produces a wrong value and reports it as verified. The worst keyed the in-flight read by applicant index rather than by person, and every application has an applicant 0, so switching applications mid-read merged one traveller's details into another's profile, past the identity cross-check, while the panel told the operator those fields needed no checking. Identity is now the traveller's own passport number, date of birth and name; an unidentifiable record yields an empty key, and an empty key matches nothing, including another empty one.