···1313The current baseline for saying "the audited suite is green" is:
14141515- `prove -lr t`
1616- - latest full green result in the realigned Meridian worktree: `Files=48, Tests=2918`
1616+ - latest full green result in the realigned Meridian worktree: `Files=48, Tests=2922`
1717- `prove -lv t/server-auth.t`
1818- `perl -c script/differential-validate`
1919- `PERLSKY_RUN_REFERENCE_DIFF=1 prove -lv t/reference-differential.t`
···5050- Local `app.bsky.*` emulation must be conservative: only synthesize owner-local feed/thread data when the PDS can answer authoritatively, and proxy upstream instead of inventing partial global state.
5151- Account email handling needs consistent normalization on write, lookup, session creation, and confirmation checks; treating email case inconsistently leaves both tests and user-facing auth behavior brittle.
5252- `com.atproto.server.requestEmailConfirmation`, `requestEmailUpdate`, and `requestAccountDelete` should reject accounts with no stored email using the official `400 InvalidRequest` / `account does not have an email address` shape, and `updateEmail` should reject unsupported syntax with the official `This email address is not supported, please use a different email.` message.
5353+- perlsky intentionally still allows test-friendly no-email local accounts, but once an email is supplied `com.atproto.server.createAccount` now follows the same unsupported-syntax rejection shape as `updateEmail` and the official runtime.
5354- `app.bsky.actor.putPreferences` and `app.bsky.notification.putPreferencesV2` now have explicit shape validation plus focused regression coverage, turning an earlier hardening concern into a pinned contract.
5455- `com.atproto.identity.resolveHandle` should reject malformed handles with `400 InvalidRequest`, not quietly treat them as misses or return a local `InvalidHandle` variant.
5556- `com.atproto.identity.resolveHandle` should treat well-formed but unresolved handles as `400 InvalidRequest` with `Unable to resolve handle`, matching the official runtime instead of returning a local `404 HandleNotFound`.
+8-2
lib/ATProto/PDS/API/Server.pm
···7777 if length($password) < 8;
7878 xrpc_error(400, 'InvalidRequest', "Password too long. Maximum length is $NEW_PASSWORD_MAX_LENGTH characters.")
7979 if length($password) > $NEW_PASSWORD_MAX_LENGTH;
8080+ my $email = undef;
8181+ if (defined($body->{email}) && length($body->{email})) {
8282+ $email = _supported_email($body->{email});
8383+ xrpc_error(400, 'InvalidRequest', 'This email address is not supported, please use a different email.')
8484+ unless defined $email;
8585+ }
80868187 my $invite;
8288 if (defined($body->{inviteCode}) && length($body->{inviteCode})) {
···135141 account_id => $account_id,
136142 did => $did,
137143 handle => $handle,
138138- email => $body->{email},
139139- email_confirmed_at => _initial_email_confirmed_at($c, $body->{email}),
144144+ email => $email,
145145+ email_confirmed_at => _initial_email_confirmed_at($c, $email),
140146 password_hash => $password_record->{hash},
141147 password_salt => $password_record->{salt},
142148 deactivated_at => $deactivated_at,