···255255256256### Testing surface
257257258258-Stages are written against narrow async traits for their IO surface (`DidResolver`, `HttpClient`, `WebSocketClient`, `PlcLogFetcher`). Unit tests supply fakes that replay recorded fixtures captured from real labelers (e.g., `mod.bsky.app`). Rendered `LabelerReport` output is locked down with `insta` snapshots for the golden path and one snapshot per named failure case, so any drift in diagnostics is caught automatically.
258258+Stages are written against narrow async traits for their IO surface (`DidResolver`, `HttpClient`, `WebSocketClient`, `PlcLogFetcher`). Unit tests supply fakes that replay recorded fixtures captured from real labelers (e.g., `moderation.bsky.app`). Rendered `LabelerReport` output is locked down with `insta` snapshots for the golden path and one snapshot per named failure case, so any drift in diagnostics is caught automatically.
259259260260## Existing Patterns
261261···390390391391**Error classification discipline.** The distinction between `SpecViolation`, `NetworkError`, and `Advisory` is the single most important thing to get right for this tool's usefulness. A user debugging their own labeler needs network failures called out separately so they can re-run with connectivity fixed; a user checking another team's labeler needs advisories (empty labels, rotated keys) visibly separated from real violations. Every new check added to the suite must explicitly pick a severity — there is no default.
392392393393-**Canonicalization risk.** DRISL-CBOR label canonicalization is subtle (strict map key ordering, minimal integer encoding, no floats/indefinite-length items). Phase 6 should validate the canonicalizer against labels fetched from a known-good production labeler (e.g., `mod.bsky.app`) before declaring the crypto stage done, to avoid false-positive `Fail` results caused by canonicalization drift rather than real signature problems.
393393+**Canonicalization risk.** DRISL-CBOR label canonicalization is subtle (strict map key ordering, minimal integer encoding, no floats/indefinite-length items). Phase 6 should validate the canonicalizer against labels fetched from a known-good production labeler (e.g., `moderation.bsky.app`) before declaring the crypto stage done, to avoid false-positive `Fail` results caused by canonicalization drift rather than real signature problems.
394394395395**Future extensibility (not in scope for this design):** The layered-pipeline pattern is intentionally feature-local. If future `test` subcommands (`test pds`, `test feed-generator`) share checks with `test labeler` — most plausibly DID/PDS resolution — they will extract into `common::identity` rather than growing a general-purpose check registry. A shared registry is explicitly YAGNI territory until a second feature demands it.
396396