Skip to main content

max / alloy

Send Enter from the username to the password, not past it Enter submits from whichever field has focus, so pressing it on the username is how the account form gets walked when nothing has been typed yet. Validation refused, correctly, and then put the caret on `confirm` with `password` still empty above it: the screen asked for the second half of a pair whose first half did not exist. The rule was written for the mismatch case, where the confirm is the right place to land because the typo is far more often in the second box. Both complaints came out of one branch and got one answer. Now the empty password focuses the password and a mismatch still focuses the confirm, which is the split `encrypt_disk` has had all along on the same shape of screen. The test that covered this asserted the wrong field, so it passed the whole time. Corrected rather than added to: the mismatch half already had its own test. Found on fw12.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-09 22:50 UTC
Signed with PGP, not checked
Commit: e832f97130986476509f32fea849f64bac54c416
Parent: ec402dc
1 file changed, +23 insertions, -5 deletions
@@ -3119,10 +3119,23 @@
3119 3119 }
3120 3120 if let Err(message) = validate_password(self.password.value(), self.confirm.value()) {
3121 3121 self.error = Some(message);
3122 - // Focus the confirm rather than the password: a mismatch is far
3123 - // more often a typo in the second one, and landing there means the
3124 - // fix is to retype the field already under the caret.
3125 - self.fields.focus(FIELD_CONFIRM);
3122 + // Which field is at fault depends on which complaint it is, and
3123 + // getting this wrong sent people to the wrong box. Enter from the
3124 + // username field is how the form is walked when nothing has been
3125 + // typed yet, and it landed the caret on `confirm` with `password`
3126 + // still empty above it: the screen asked for the second half of a
3127 + // pair whose first half did not exist. A mismatch is different, and
3128 + // there the confirm is right, because a mismatch is far more often
3129 + // a typo in the second one and the fix is to retype the field
3130 + // already under the caret.
3131 + //
3132 + // Same split as [`encrypt_disk`], which had it right; this screen
3133 + // is the one that did not.
3134 + self.fields.focus(if self.password.value().is_empty() {
3135 + FIELD_PASSWORD
3136 + } else {
3137 + FIELD_CONFIRM
3138 + });
3126 3139 return Flow::Continue;
3127 3140 }
3128 3141 if let Err(message) = validate_pubkey(self.pubkey.value()) {
@@ -4916,7 +4929,12 @@
4916 4929
4917 4930 assert_eq!(view.step(), Step::Account, "advanced without a password");
4918 4931 assert_eq!(view.answers.username, None);
4919 - assert_eq!(view.fields.current(), FIELD_CONFIRM);
4932 + // The password, not the confirm. This is the walk-the-form case: Enter
4933 + // from the username with nothing else typed, where sending the caret
4934 + // past an empty password to the box under it asks for the second half
4935 + // of a pair whose first half does not exist. Found on fw12, 2026-08-09,
4936 + // by someone doing exactly that.
4937 + assert_eq!(view.fields.current(), FIELD_PASSWORD);
4920 4938 assert!(view.error.unwrap().contains("password is required"));
4921 4939 }
4922 4940