···1212The immediate goal is a PDS that is pleasant to hack on and interoperable enough to be exercised with real AT Protocol clients and repo sync tooling.
13131414Developer-oriented notes about the current facade/module split live in `docs/CODE_STRUCTURE.md`.
1515+Endpoint-by-endpoint conformance status lives in `docs/ENDPOINT_CONFORMANCE.md`.
15161617Reference differential validation:
1718
+71
docs/ENDPOINT_CONFORMANCE.md
···11+# Endpoint Conformance
22+33+As of 2026-03-12, `perlsky` has three broad classes of external surface:
44+55+- `executable reference-differenced`
66+ These endpoints or behaviors are compared directly against the official `@atproto/pds` runtime through `script/differential-validate`.
77+- `locally regression-tested only`
88+ These endpoints are covered in `t/*.t`, but the current official runtime either does not expose the same surface or is not a useful like-for-like oracle.
99+- `intentional local divergence`
1010+ These are deliberate product or operator choices, not accidental compatibility gaps.
1111+1212+## Executable Reference-Differenced
1313+1414+The current differential harness directly compares:
1515+1616+- core account/session flows
1717+ `describeServer`, `createAccount`, `createSession`, `getSession`, `refreshSession`, `deleteSession`, app-password lifecycle, password-boundary behavior, and email/account-delete flows
1818+- identity flows
1919+ `resolveHandle`, `resolveDid`, `resolveIdentity`, PLC credential/signature/update flows, and handle-conflict semantics
2020+- repo and sync flows
2121+ `createRecord`, `putRecord`, `deleteRecord`, `applyWrites`, `listRecords`, `getRecord`, `getLatestCommit`, `getHead`, `getBlocks`, `getRepo`, `getCheckout`, `listBlobs`, `getBlob`, `listRepos`, `listMissingBlobs`, and `importRepo`
2222+- moderation/admin flows
2323+ `updateSubjectStatus`, `getSubjectStatus`, `getAccountInfo`, `getAccountInfos`, `sendEmail`, `updateAccountEmail`, `updateAccountHandle`, `updateAccountPassword`, `deleteAccount`, `disableAccountInvites`, `enableAccountInvites`, `disableInviteCodes`, and admin invite-code listing semantics
2424+- eventing and crawler behavior
2525+ `subscribeRepos`, firehose cursor behavior, and outbound crawler notification semantics after local repo activity
2626+2727+For the exact current comparison set, use:
2828+2929+```sh
3030+script/differential-validate
3131+PERLSKY_DIFF_ACCOUNT_DID_METHOD=did:plc script/differential-validate
3232+```
3333+3434+## Locally Regression-Tested Only
3535+3636+These surfaces are covered locally but are not currently executable-differenced because the official runtime does not expose the same endpoint or does not provide a like-for-like comparison surface:
3737+3838+- `com.atproto.sync.requestCrawl`
3939+ The reference runtime still drives crawler notices internally, but the current official build does not expose the endpoint directly as a comparable external surface.
4040+- `com.atproto.sync.notifyOfUpdate`
4141+ Locally covered in `t/uncovered-endpoints.t`; not wired as a comparable public surface in the current official runtime.
4242+- `com.atproto.admin.updateAccountSigningKey`
4343+ Locally covered and aligned to lexicon-void response semantics; no matching official external endpoint wiring was found in the current reference build.
4444+- `com.atproto.admin.searchAccounts`
4545+ Implemented and tested locally; not exposed by the current official runtime.
4646+- `com.atproto.sync.listReposByCollection`
4747+ Present in the published lexicon and covered locally, but not exposed by the current official runtime.
4848+- `com.atproto.temp.*`
4949+ `requestPhoneVerification`, `revokeAccountCredentials`, `checkSignupQueue`, `dereferenceScope`, and `fetchLabels` are locally covered but do not have a like-for-like official public comparison surface.
5050+- local label RPCs
5151+ `com.atproto.label.queryLabels`, `subscribeLabels`, and `com.atproto.temp.fetchLabels` are verified locally; the official runtime does not provide a directly comparable local-labeler implementation.
5252+- local appview emulation
5353+ `app.bsky.actor.getPreferences`, `putPreferences`, `notification.getPreferences`, `notification.putPreferencesV2`, and the conservative local feed/thread/profile fallback behavior are locally regression-tested rather than compared against an official self-hosted AppView implementation.
5454+5555+## Intentional Local Divergences
5656+5757+These are currently deliberate:
5858+5959+- admin bearer shortcut
6060+ `perlsky` still accepts a local bearer-style admin secret in addition to strict Basic auth.
6161+- testing-friendly email toggles
6262+ `testing_allow_unauthenticated_email_confirm` and `testing_auto_confirm_email` intentionally keep no-SMTP testing practical.
6363+- self-service invite issuance
6464+ Disabled by default, but available behind `self_service_invite_codes`.
6565+- `applyWrites` missing-delete error shape
6666+ The official runtime still returns `500 InternalServerError` for one missing-delete path; `perlsky` intentionally keeps the cleaner `400 InvalidRequest`.
6767+6868+## Reading This With `docs/TEST_AUDIT.md`
6969+7070+- Use this file to answer “is this surface reference-differenced, local-only, or intentionally different?”
7171+- Use [TEST_AUDIT.md](./TEST_AUDIT.md) to answer “what specific behaviors were audited, fixed, or are still known gaps?”