test(ingestion): pin the handle-flood root causes and the fetch taxonomy master
Four reds; T0 fully green and no pre-existing T1 breakage. 1. communities: the profile record CreateCommunity writes carries no `handle`. Read back via getRecord — the failure dumps the actual record, so the evidence is in the output rather than in an argument. Without the field the consumer must resolve one from the DID document, which on an egress-blocked stack yields "handle.invalid" into a UNIQUE column. 2. jetstream: a profile event whose handle is held by a DIFFERENT DID is currently swallowed as an idempotent replay and returns nil. Pinned as a PERMANENT refusal naming the contested handle, with the community absent and the incumbent untouched. Permanent is the load-bearing half: left transient it costs ~4.2s of inline blocking plus ten redrives per delivery, which is the flood. The other half of the narrowing is pinned beside it and PASSES today — a same-DID replay must stay a silent no-op. A fix that widened the refusal to every conflict would dead-letter every community's profile on every cursor rewind, and nothing else in the suite would notice. 3. jetstream: a genuine XRPC RecordNotFound from the author's PDS is classified transient today; pinned as permanent, with the httptest request count asserting one event produces exactly one fetch. Bare 404 and 5xx are pinned as transient and PASS today — they are the guards that stop the fix over-reaching, since a bare 404 usually means the request never reached a PDS at all (users.FetchProfileRecord draws the same distinction). Scope note: driving HandleEvent directly, no dead-letter row is written and no redrive runs — the consumer never touches that table, the connector does. These pin the input the connector switches on (the ErrPermanentEvent wrapping) plus the absence of any retry loop inside the consumer/fetcher. The connector's half is TestConnector_DeadLettersAfterRetryExhaustion. 4. jetstream: a community whose handle resolves to "handle.invalid" must not be stored, classified TRANSIENT (the PLC may be unreachable now and fine in a minute). Mirrors authorpost.go's user-path guard. No conflict with fix 2 — different causes, composing guards: this one refuses before the insert, that one refuses a collision the insert reports. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>