# Queue jobs include campaign and recipient identifiers for traceability

- **Pattern:** `ab-002459` (`sending-pipeline-infrastructure.queue-architecture.job-traceability`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002459
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002459)

## Why it matters

A job payload containing only `{to, subject, html}` becomes useless the moment it lands in the DLQ. Without a campaign ID, a recipient ID, and a correlation key, you cannot answer which campaign generated the failure, which contact never received the message, or whether the failure was isolated or systemic. ISO-25010:2011 maintainability.analysability requires that system faults can be diagnosed from the available artifacts — an untraceable job payload defeats that entirely, forcing triage through raw log searches across worker instances.

## Severity rationale

High because untraceable failed jobs in a DLQ cannot be attributed, replayed selectively, or used to satisfy delivery audit requirements without manual cross-referencing of logs.

## Remediation

Include all three traceability fields at the point of enqueue in `lib/queue.ts` or wherever campaign sends are dispatched:

```ts
await emailQueue.add('send', {
  campaignId: campaign.id,
  recipientId: contact.id,
  correlationId: `${campaign.id}:${contact.id}`,
  templateId: template.id,
  to: contact.email,
  mergeFields: { firstName: contact.firstName }
})
```

In the worker, log all three fields on both success and failure paths so correlation IDs appear in structured log queries.

## Detection

- **ID:** `job-traceability`
- **Severity:** `high`
- **What to look for:** Enumerate all required traceability fields in the job payload: (1) campaign/template ID, (2) recipient ID or email, (3) correlation ID or composite key. Count how many of these 3 fields are present in the job payload structure. Also check that the worker logs include these fields on both success and failure paths.
- **Pass criteria:** Every email job payload includes all 3 traceability fields: a campaign/template identifier, a recipient identifier, and either a correlation ID or a composite key (campaign + recipient) that uniquely identifies the send attempt. These fields are logged by the worker on both success and failure. Report even on pass: list the 3 traceability fields found and their locations.
- **Fail criteria:** Job payloads contain only the bare minimum (email address and body) with no campaign identifier. Failed jobs in the DLQ cannot be attributed to a specific campaign. Worker logs do not include campaign or recipient IDs. Or fewer than 3 traceability fields are present.
- **Skip (N/A) when:** The application sends only single one-off emails with no campaigns, sequences, or batch sends — confirmed by the absence of campaign models.
- **Detail on fail:** `"Job payload only contains {to, subject, html} — no campaign_id or recipient_id. DLQ jobs untraceable"` or `"Worker logs email address only — no campaign context in error logs"`
- **Remediation:** Include traceability fields in every job payload:

  ```ts
  // When enqueuing
  await emailQueue.add('send', {
    campaignId: campaign.id,
    recipientId: contact.id,
    correlationId: `${campaign.id}:${contact.id}`,
    templateId: template.id,
    to: contact.email,
    mergeFields: { ... }
  })

  // In worker — log with context
  logger.info({ campaignId, recipientId, correlationId }, 'Email sent successfully')
  logger.error({ campaignId, recipientId, correlationId, error }, 'Email send failed')
  ```

  For queue depth monitoring and alerting, see the Operational Resilience (Email) Audit which covers queue observability in detail.

## External references

- iso-25010 maintainability.analysability

Taxons: observability

HTML version: https://auditbuffet.com/patterns/ab-002459
