# Communication queue

## Purpose

The `comm_queue` table stores SMS and email notifications before a scheduled worker sends them to an external provider. The workers are invoked by the CLI wrappers in `application/scheduled_tasks` and the scheduled-tasks controller rejects non-CLI requests in its constructor. If a queued email declares an attachment, the worker treats a missing or unreadable file as a failed attempt and does not send an incomplete message without that attachment.

## Processing rules

- A worker selects active records for its channel whose status is `waiting`. It can also select failed records when retries are enabled, subject to the configured attempt limit and retry delay.
- Selecting candidates does not reserve them. Immediately before an external send, the model atomically changes one candidate from `waiting` or `failed` to `processing`, records the process ID and processing time, and increments `attempts` in the same conditional update.
- A worker sends a notification only if that conditional update changed exactly one row. A concurrent worker that selected the same candidate cannot claim it after its status has become `processing`, so it does not repeat the external send.
- A successful provider call changes the record to `processed`. Validation or provider errors change it to `failed`, allowing the retry rules to decide whether and when it may be selected again.

The claim prevents concurrent workers from sending the same queue record at the same time. It cannot provide exactly-once delivery if the process terminates after the provider accepts a message but before the success state is stored; provider idempotency would be required to cover that failure window.

## Administrative preview

The message details screen renders queued email HTML inside a sandboxed `iframe`. This preserves the formatting that the recipient sees without inserting the stored HTML into the administration document. The sandbox blocks scripts and access to the parent page, while the no-referrer policy prevents remote email resources from receiving the administration URL. SMS content remains a plain-text preview.

## Configuration

Queue batch sizes, retry attempt limits, and retry delays are read from the existing `PROCESS_SMS_QUEUE_*` and `PROCESS_EMAIL_QUEUE_*` constants. This change does not require new configuration or a database migration and does not alter historical records.

## Verification

1. Start two workers while one eligible queue record exists.
2. Verify that only one conditional claim changes the record to `processing` and increments `attempts`.
3. Verify that the other worker reports the record as unavailable and does not call the SMS or SMTP provider.
4. Verify that a successful send ends in `processed`, while a provider failure ends in `failed` and remains governed by the configured retry limit and delay.
5. Open an email record in the administration panel and verify that its HTML formatting is rendered while embedded scripts cannot execute or access the parent page.
