···11# Test Audit Status
2233-As of 2026-03-12, the focused test-correctness and reference-audit pass is complete on rewritten history through the current post-`6f181ab` conformance sweep.
33+As of 2026-03-12, the focused test-correctness and reference-audit pass is complete on rewritten history through the current overnight conformance sweep.
4455That does not mean every test has been manually revalidated against every other PDS implementation line by line. It means:
66···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=2943`
1616+ - latest full green result in the realigned Meridian worktree: `Files=48, Tests=2950`
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`
···5454- `com.atproto.server.createAccount` must not turn duplicate-email requests into a `500`; it now follows the official client-visible `400 InvalidRequest` / `Email already taken: ...` shape instead.
5555- Local handle-conflict flows now use the reference runtime’s client-visible `400 InvalidRequest` / `Handle already taken: ...` shape on `createAccount`, `com.atproto.identity.updateHandle`, and `com.atproto.admin.updateAccountHandle`, instead of the older local `HandleNotAvailable` variant.
5656- The executable differential harness now proves that handle-conflict shape directly for both user and admin handle-update flows, not just local regression tests.
5757+- `com.atproto.server.createSession` invalid-credential failures now use the reference runtime’s `401 AuthenticationRequired` shape instead of the older local `AuthRequired` variant.
5858+- `com.atproto.admin.sendEmail` now follows the reference runtime’s `400 InvalidRequest` / `Recipient not found` shape for a missing recipient instead of returning a local `404 AccountNotFound`.
5959+- `com.atproto.admin.updateAccountPassword` follows the reference runtime’s looser admin policy: it rejects overlong passwords with `400 InvalidRequest` / `Invalid password length.`, but does not impose the normal user-facing minimum-length gate.
6060+- `com.atproto.admin.disableAccountInvites` / `enableAccountInvites` now ignore the local `note` field so the visible account state matches the official runtime instead of carrying an extra stored `inviteNote`.
5761- `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.
5862- `com.atproto.identity.resolveHandle` should reject malformed handles with `400 InvalidRequest`, not quietly treat them as misses or return a local `InvalidHandle` variant.
5963- `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`.
···78827983- Email confirmation remains testing-friendly only behind the explicit `testing_allow_unauthenticated_email_confirm` / `testing_auto_confirm_email` toggles because email sending is not configured in the current environment.
8084- Admin auth still accepts a local bearer-token shortcut, while the official reference PDS expects Basic auth with `admin` credentials.
8585+- `com.atproto.admin.searchAccounts` remains locally implemented and regression-tested, but the current official runtime does not wire that endpoint at all, so it stays documented as a local extension instead of executable-reference-differenced.
8186- Self-service invite creation exists only behind `self_service_invite_codes`; default behavior is admin-only invite minting.
8287- Label RPC parity is covered locally, but there is no like-for-like official local-labeler surface to diff against in the same way as core PDS endpoints.
8388
+5-4
lib/ATProto/PDS/API/Admin.pm
···1717use ATProto::PDS::Moderation qw(current_record_subject current_subject_status parse_at_uri);
18181919our @EXPORT_OK = qw(register_admin_handlers);
2020+my $NEW_PASSWORD_MAX_LENGTH = 256;
20212122sub register_admin_handlers ($registry, $app) {
2223 $registry->register('com.atproto.admin.getAccountInfo', sub ($c, $endpoint) {
···110111 require_admin($c);
111112 my $body = $c->req->json || {};
112113 my $account = $c->store->get_account_by_did($body->{recipientDid} // q());
113113- xrpc_error(404, 'AccountNotFound', 'Recipient was not found') unless $account;
114114+ xrpc_error(400, 'InvalidRequest', 'Recipient not found') unless $account;
114115 xrpc_error(400, 'InvalidRequest', 'account does not have an email address')
115116 unless defined($account->{email}) && length($account->{email});
116117 my $subject = defined($body->{subject}) && length($body->{subject})
···159160 $registry->register('com.atproto.admin.updateAccountPassword', sub ($c, $endpoint) {
160161 require_admin($c);
161162 my $body = $c->req->json || {};
162162- xrpc_error(400, 'InvalidPassword', 'Passwords must be at least 8 characters long')
163163- if length($body->{password} // q()) < 8;
163163+ xrpc_error(400, 'InvalidRequest', 'Invalid password length.')
164164+ if length($body->{password} // q()) > $NEW_PASSWORD_MAX_LENGTH;
164165 my $account = $c->store->get_account_by_did($body->{did} // q());
165166 xrpc_error(404, 'AccountNotFound', 'Account was not found') unless $account;
166167 my $password_record = hash_password($body->{password});
···383384 $c->store->update_account(
384385 $account->{did},
385386 invites_disabled => $disabled ? 1 : 0,
386386- invite_note => $note,
387387+ invite_note => undef,
387388 );
388389 return {};
389390}