···8686| `t/crypto-interop.t` | direct reference differential | pinned upstream crypto fixture coverage |
8787| `t/email-confirmation.t` | audited local regression | intentionally testing-friendly email flow |
8888| `t/event-stream.t` | audited local regression | wire-format, malformed frame, and event decoding coverage |
8989-| `t/extended-api.t` | audited local regression | broad XRPC behavior including invites and moderation-adjacent flows |
9090-| `t/external-surface.t` | audited local regression | external repo/account surface including missing-blob behavior; still mixes surface inventory with conformance assertions and should stay documented that way |
8989+| `t/extended-api.t` | audited local regression | broad XRPC behavior including invites and moderation-adjacent flows; still intentionally mixes conformance-ish happy paths with local-policy coverage |
9090+| `t/external-surface.t` | audited local regression | external repo/account surface including missing-blob behavior; intentionally broad, with order-insensitive assertions for label presence rather than brittle label ordering |
9191| `t/firehose.t` | audited local regression | repo subscription lifecycle, cursor, and CAR behavior |
9292| `t/identity.t` | local correctness/infrastructure | handle and DID identity flow coverage |
9393-| `t/import-repo.t` | audited local regression | import/snapshot restore behavior, including perlsky's intentionally tolerant malformed-record import semantics |
9393+| `t/import-repo.t` | audited local regression | import/snapshot restore behavior, including perlsky's intentionally tolerant malformed-record import semantics and explicit rollback to the imported snapshot |
9494| `t/invite-gating.t` | audited local regression | self-service invite flag behavior |
9595| `t/ipld-canonical.t` | local correctness/infrastructure | canonical IPLD encoding invariants |
9696| `t/ipld-codecs.t` | local correctness/infrastructure | DAG-CBOR and codec coverage |
···1351352. extend `script/differential-validate` where automation is cheap and high value
1361363. add a written mapping from each local-only suite to the protocol or product invariant it is meant to protect
1371374. decide whether to tighten admin auth to reference semantics or document the bearer shortcut as a permanent extension
138138-5. move the testing-friendly email confirmation path behind an explicit smoke/dev switch instead of ambient behavior
138138+5. keep local testing-only toggles, like the email-confirmation bypass, pinned in focused suites instead of letting broad mixed suites depend on them implicitly
1391396. keep narrowing the local `ServiceProxy` surface until every locally answered `app.bsky.*` field is either authoritative or explicitly documented as a local-only extension
1401407. keep documenting broad suites like `t/extended-api.t`, `t/external-surface.t`, and `t/import-repo.t` as mixed conformance-plus-product coverage rather than over-claiming that every assertion is a pure reference check
141141
+36-10
t/extended-api.t
···271271272272$t->get_ok(Mojo::URL->new('/xrpc/com.atproto.label.queryLabels')->query(
273273 uriPatterns => "at://$did*",
274274-))->status_is(200)
275275- ->json_is('/labels/0/val', '!hide');
274274+))->status_is(200);
275275+ok(
276276+ _find_label($t->tx->res->json->{labels}, val => '!hide'),
277277+ 'queryLabels includes the takedown label',
278278+);
276279277280$t->get_ok('/xrpc/com.atproto.temp.fetchLabels?limit=10')
278278- ->status_is(200)
279279- ->json_is('/labels/0/val', '!hide');
281281+ ->status_is(200);
282282+ok(
283283+ _find_label($t->tx->res->json->{labels}, val => '!hide'),
284284+ 'fetchLabels includes the takedown label',
285285+);
280286281287$t->post_ok('/xrpc/com.atproto.admin.updateSubjectStatus' => {
282288 Authorization => $admin_auth,
···287293288294$t->get_ok(Mojo::URL->new('/xrpc/com.atproto.label.queryLabels')->query(
289295 uriPatterns => "at://$did*",
290290-))->status_is(200)
291291- ->json_is('/labels/0/val', '!hide')
292292- ->json_is('/labels/0/neg', JSON::PP::true);
296296+))->status_is(200);
297297+ok(
298298+ _find_label($t->tx->res->json->{labels}, val => '!hide', neg => JSON::PP::true),
299299+ 'queryLabels includes the negated takedown label',
300300+);
293301294302$t->get_ok('/xrpc/com.atproto.temp.fetchLabels?limit=10')
295295- ->status_is(200)
296296- ->json_is('/labels/0/val', '!hide')
297297- ->json_is('/labels/0/neg', JSON::PP::true);
303303+ ->status_is(200);
304304+ok(
305305+ _find_label($t->tx->res->json->{labels}, val => '!hide', neg => JSON::PP::true),
306306+ 'fetchLabels includes the negated takedown label',
307307+);
298308299309$t->post_ok('/xrpc/com.atproto.sync.requestCrawl' => json => {
300310 hostname => 'relay.example.test',
···310320 ->json_is('/hostname', 'relay.example.test');
311321312322done_testing;
323323+324324+sub _find_label {
325325+ my ($labels, %expected) = @_;
326326+ return 0 unless ref($labels) eq 'ARRAY';
327327+ for my $label (@$labels) {
328328+ next unless ref($label) eq 'HASH';
329329+ my $matches = 1;
330330+ for my $key (keys %expected) {
331331+ next if defined($label->{$key}) && "$label->{$key}" eq "$expected{$key}";
332332+ $matches = 0;
333333+ last;
334334+ }
335335+ return 1 if $matches;
336336+ }
337337+ return 0;
338338+}
+21-3
t/external-surface.t
···234234235235$t->get_ok(Mojo::URL->new('/xrpc/com.atproto.label.queryLabels')->query(
236236 uriPatterns => $record_uri,
237237-))->status_is(200)
238238- ->json_is('/labels/0/val' => '!hide')
239239- ->json_is('/labels/0/uri' => $record_uri);
237237+))->status_is(200);
238238+ok(
239239+ _find_label($t->tx->res->json->{labels}, val => '!hide', uri => $record_uri),
240240+ 'queryLabels includes the record takedown label',
241241+);
240242241243for my $cid ($blob_cid, $nested_blob_cid) {
242244 $app->store->dbh->do(
···277279 ->json_is('/cursor' => $missing_cids[1]);
278280279281done_testing;
282282+283283+sub _find_label {
284284+ my ($labels, %expected) = @_;
285285+ return 0 unless ref($labels) eq 'ARRAY';
286286+ for my $label (@$labels) {
287287+ next unless ref($label) eq 'HASH';
288288+ my $matches = 1;
289289+ for my $key (keys %expected) {
290290+ next if defined($label->{$key}) && "$label->{$key}" eq "$expected{$key}";
291291+ $matches = 0;
292292+ last;
293293+ }
294294+ return 1 if $matches;
295295+ }
296296+ return 0;
297297+}