···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=61, Tests=3128`
1616+ - latest full green result in the realigned Meridian worktree: `Files=61, Tests=3129`
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`
···7777- 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.
7878- 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.
7979- Missing-repo read paths now match the official runtime more closely: `describeRepo`, `sync.getLatestCommit`, `sync.getRecord`, `sync.getRepo`, `sync.getCheckout`, `sync.getHead`, `sync.getRepoStatus`, `sync.getBlocks`, `sync.getBlob`, and `sync.listBlobs` report `400 RepoNotFound`, while `listRecords` reports `400 InvalidRequest` / `Could not find repo: ...`.
8080+- `com.atproto.sync.getBlob` needs two distinct not-found branches to stay reference-compatible: missing repos still report `400 RepoNotFound`, but blobs that are merely unreferenced for that repo report `400 InvalidRequest` / `Blob not found`, while moderation takedowns still hide them behind the moderation-layer `404 BlobNotFound` path.
8081- `com.atproto.repo.getRecord` must honor `cid` when present, and `putRecord` / `deleteRecord` must actually enforce `swapRecord`; those negative edges are now covered directly.
8182- `com.atproto.repo.createRecord` follows the reference runtime by ignoring a stray `swapRecord` field, and direct reference coverage now pins `putRecord` / `deleteRecord` `swapCommit` and `swapRecord` mismatch semantics explicitly.
8283- App-password sessions follow the official runtime more closely than the older local assumptions did: access-token scopes use the `com.atproto.appPass` / `com.atproto.appPassPrivileged` names, standard app-password sessions may list app passwords, privileged-only `getServiceAuth` failures report `InvalidRequest`, and revoked refresh tokens on `refreshSession` fail with `400 ExpiredToken`.
+3-3
lib/ATProto/PDS/API/Sync.pm
···114114 $registry->register('com.atproto.sync.getBlob', sub ($c, $endpoint) {
115115 my $account = _repo_by_did_or_error($c, missing_status => 400);
116116 my $blob = $c->store->get_blob($c->param('cid') // q());
117117- xrpc_error(404, 'BlobNotFound', 'Blob was not found')
118118- unless $blob && $c->store->blob_owned_by_did($c->param('cid') // q(), $account->{did});
117117+ xrpc_error(400, 'InvalidRequest', 'Blob not found')
118118+ unless $blob && $c->store->blob_referenced_by_did($c->param('cid') // q(), $account->{did});
119119 assert_blob_readable($c, $account, $blob);
120120- xrpc_error(404, 'BlobNotFound', 'Blob content is not available')
120120+ xrpc_error(400, 'InvalidRequest', 'Blob not found')
121121 unless $blob->{storage_path} && -f $blob->{storage_path};
122122 open(my $fh, '<:raw', $blob->{storage_path}) or xrpc_error(500, 'StorageFailure', 'Unable to read blob');
123123 local $/ = undef;
+12
lib/ATProto/PDS/Store/SQLite.pm
···758758 ) // 0);
759759}
760760761761+sub blob_referenced_by_did ($self, $cid, $did) {
762762+ return 0 unless defined $cid && length $cid && defined $did && length $did;
763763+ return !!($self->dbh->selectrow_array(
764764+ q{SELECT 1 FROM blob_owners WHERE cid = ? AND did = ? AND referenced_at IS NOT NULL},
765765+ undef,
766766+ $cid,
767767+ $did,
768768+ ) // 0);
769769+}
770770+761771sub mark_blobs_referenced ($self, $did, @cids) {
762772 if (!defined($did) || ref($did) || (!length($did) && @cids)) {
763773 unshift @cids, $did if defined $did;
···856866 did collection rkey cid value_json record_bytes repo_rev created_at updated_at
857867 )], qw(record_bytes)),
858868 );
869869+ $self->mark_blobs_referenced($did, _record_blob_cids($args{value}));
859870860871 return $self->get_record($did, $collection, $rkey);
861872}
···886897 did collection rkey cid value_json record_bytes repo_rev created_at updated_at
887898 )], qw(record_bytes)),
888899 );
900900+ $self->mark_blobs_referenced($did, _record_blob_cids($record->{value}));
889901 }
890902 return 1;
891903}