feat(canopy-mq): durable consumer-side inbox + DLQ + replay endpoint

External review (2026-05-09) flagged that canopy has the producer half of the durable-events story (event_outbox per ADR-018, #388 (closed)) but lacks the consumer half. CRAIG has the matching pieces:

  • crates/craig-mq/src/inbox.rs — durable inbox table with idempotent-event recording
  • crates/craig-mq/src/subscriber.rs:250 — retry/backoff + DLQ wiring
  • crates/craig-api/src/admin_replay.rs — admin endpoint for replaying DLQ'd events

Scope

  • Per-subscriber event_inbox table (event_id PK, payload, processed_at, attempts, last_error). Inserted in the same transaction as the consumer's domain write so events are processed exactly-once at the database level.
  • Subscriber retry policy: exponential backoff up to N attempts before DLQ.
  • Dead-letter queue (separate RabbitMQ exchange + per-service queue).
  • POST /v1/admin/events/replay endpoint per consumer service to re-process DLQ'd events after operator triage.

Refs

External review (2026-05-09 IdP/architecture audit). Companion to #388 (closed) (outbox).