Users standing at a register saw nothing until the detailed ack, which
waits on extraction (10-60s) and reads as a black hole; failures could
take minutes longer via the sweep. Now the webhook reacts with a U+2705
checkmark on the sender's own media bubble right after the durable row
is persisted, so the 'correctly received' signal lands within seconds.
- sendReaction in graph-api: best-effort like mark-read, never throws,
no outbound row (a reaction is not a message in the conversation model)
- gated on the chat MIME allowlist (moved to lib/chat-mime.ts so tests
mocking process-inbound cannot lose it): junk earns M15, no checkmark
- no reaction for unknown, muted, or redelivered (23505) messages
- detailed M4/M5 combined ack and the one-message-per-burst design are
unchanged
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>