## What
Adds a pluggable compressor registry and a `headroom.compressor`
entry-point group so compressors can be registered, discovered, and
selected by name.
- Pure-data contract (`CompressorDescriptor`, `CompressInput`,
`CompressOutput`, `Compressor` Protocol). Only plain types
(`str`/`int`/`bool`/`list`/`dict`) cross the boundary — no tokenizer,
store, or config objects — so the same contract can be implemented
outside Python.
- `CompressorRegistry`: starts empty and accepts explicit registrations
by name; discovers external compressors from the `headroom.compressor`
group fail-open (mirrors the existing pipeline-extension discovery);
resolves an opt-in selection (`select`/`active`) — nothing active by
default, `"*"` for all, otherwise a name allowlist with unknown names
logged and skipped. Discovery loads compressors but never invokes
`compress`.
## Why
Compressors are currently constructed and dispatched via a hardcoded
chain in the content router; there is no way to add or select one
without editing the router. This lands a name-addressable seam so that
becomes possible.
## Behavior change
None. Purely additive — not wired into `content_router`, the proxy
server, or config, and constructing the registry has no global side
effects. Router integration is a deliberate follow-up.
## Testing
- `pytest tests/test_compressor_registry.py -q` → 11 passed (contract
round-trip, registration, opt-in selection semantics, wildcard,
unknown-name skip, monkeypatched entry-point discovery,
discovery-never-runs-compress).
- `ruff check` / `ruff format` → clean; `mypy` → no issues.