···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- `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.
5353- `com.atproto.identity.resolveHandle` should reject malformed handles with `400 InvalidHandle`, not quietly treat them as misses.
5454+- `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`.
5455- Remote `did:web` DID docs, conservative `resolveIdentity` handle validation, and external handle adoption all need explicit coverage because small resolver-policy drifts turn into visible interop bugs quickly.
5556- Remote `did:plc` DID docs should resolve through the PLC directory defaults even when `plc_url` is not explicitly configured; gating that path on local config silently breaks federated identity lookups.
5657- `com.atproto.repo.getRecord` must honor `cid` when present, and `putRecord` / `deleteRecord` must actually enforce `swapRecord`; those negative edges are now covered directly.
+3-3
lib/ATProto/PDS/API/Builtins.pm
···7474 }
75757676 die {
7777- status => 404,
7878- error => 'HandleNotFound',
7979- message => "No DID found for handle $handle",
7777+ status => 400,
7878+ error => 'InvalidRequest',
7979+ message => 'Unable to resolve handle',
8080 };
8181 });
8282
+14
script/differential-validate
···560560 check(($res->json->{did} // q()) eq $server{$name}{did}, "$name resolveHandle returns the created DID");
561561}
562562563563+for my $name (sort keys %server) {
564564+ my $res = get_form(
565565+ $server{$name}{origin},
566566+ 'com.atproto.identity.resolveHandle',
567567+ { handle => 'missing.test' },
568568+ );
569569+ $server{$name}{resolve_handle_missing} = normalize_xrpc_error($res);
570570+}
571571+572572+check(
573573+ same_hash($server{reference}{resolve_handle_missing}, $server{perlsky}{resolve_handle_missing}),
574574+ 'resolveHandle missing-handle semantics match the official reference PDS',
575575+);
576576+563577note('Comparing subscribeRepos bootstrap backfill');
564578for my $name (sort keys %server) {
565579 my $frames = frames_until_quiet("$server{$name}{origin}/xrpc/com.atproto.sync.subscribeRepos?cursor=0");