Skip to content

Durable support reply notifications ​

Migration 20261011000100_support_notification_outbox.sql records one notification intent keyed by the reply message identifier in the same transaction as the reply, send receipt, unread counters and audit. Replaying a message identifier does not create another intent. The backend starts a polling processor at startup and stops scheduling batches at shutdown.

Workers claim up to 25 due intents using row locks with SKIP LOCKED. Each claim has a fresh token and a 60-second lease. Expired claims can be reclaimed after a worker crash; old tokens cannot publish or reschedule another worker's claim. Publishing inserts the inbox row and marks the intent inbox_recorded in one database transaction. A unique source key and replayable publication command prevent duplicate inbox rows when the worker loses a successful response.

States have deliberately limited meanings:

StateMeaning
pendingReply and notification intent are committed; inbox publication is outstanding.
processingA worker holds a temporary publication claim.
retry_waitInbox publication could not be confirmed; another attempt is scheduled.
inbox_recordedThe notification was persisted in the database inbox.
recipient_unavailableThe ticket had no member recipient when a staff reply was recorded. No inbox notification is attempted.

Every outbox status and publication receipt reports providerDelivery: not_verified. The existing trigger_deliver_notification_webhook runs after notification insertion and can enqueue the deliver-notification Edge Function, which processes external channels. This outbox confirms only inbox persistence. External channel outcomes are verified separately through the existing notification_deliveries receipts; provider acceptance does not prove that a member saw a notification or received it on a device.

The retained webhook returns without dispatch when pg_net, Vault or its required secrets are unavailable. If webhook enqueueing raises an error, its AFTER INSERT trigger fails the publication transaction: both the inbox insert and the inbox_recorded transition roll back, while the previously committed reply and queue intent remain available for retry. Successful enqueueing commits with the inbox insert; later Edge Function/provider failures are outside that transaction and do not undo the inbox record. The outbox does not suppress or replace this webhook.

Migration 20261011000500_bounded_support_notification_status.sql upgrades both the bounded status function and the publication function for installations that already applied the original outbox migration. It changes the receipt marker without resetting intents, attempts, claims or recorded inbox rows.

Publication failures use bounded exponential backoff, capped at one hour, with continued retries. The queue retains attempt counts and a fixed error code rather than raw error payloads. A failed retry-state write leaves the lease available for eventual recovery. The actor-scoped support_notification_status function requires current conversation access; raw table and worker functions are not available to browser roles.

Previously recorded replies are not backfilled because their old notification outcome is unknown. Ticket creation and resolution notifications retain their existing behavior. The local PGlite tests verify transaction rollback, replay, expired-claim fencing, retry scheduling, routing and browser restrictions; native concurrent workers, a full Supabase migration replay and hosted operation still require separate release validation.

Released under Proprietary Enterprise License.