Preconditions
Organizer can access the event guest list and fixture guests include invited, confirmed, bounced, and recently-contacted states.
Happy path / Lifecycle
Open a guest or select rows in the guest list.
Single resend is available from the row actions; bulk resend appears only when selected guests are eligible.
Choose invitation or confirmation resend.
The dialog previews recipients, exclusions, message type, and the email job progress.
Send.
The single-guest resend endpoint queues the invitation and updates last-contacted/email interaction state.
Failure modes
Permission denied at the right boundary
Trigger: viewer/support attempts resend.
Resolution: 403, no message is queued, and recipient details are not exposed.
Cross-tenant isolation
Trigger: tenant A user guesses tenant B guest id.
Resolution: 404 masks guest existence and sends nothing.
Soft-delete leaves audit trail
Trigger: organizer removes a scheduled resend from queue.
Resolution: resend job is cancelled/tombstoned with audit row and prior queued state.
Archive vs delete distinction
Trigger: event or guest is archived.
Resolution: archived items block resend with reversible-state copy; deleted items are unavailable and destructive.
Edit lock during publish
Trigger: publish/email template snapshot occurs while resend dialog is open.
Resolution: send uses the published snapshot or blocks with conflict copy; it never sends mixed template state.
Audit row on every state change
Trigger: resend requested, queued, cancelled, sent, failed, or retried.
Resolution: audit rows record actor, guest id, message type, and outbox id.
Two organizers concurrent
Trigger: two organizers resend to the same guest simultaneously.
Resolution: idempotency prevents duplicate email within the resend window and both sessions see final status.
Undo window for destructive actions
Trigger: organizer cancels a queued resend.
Resolution: 10 second undo restores the queued job when delivery has not started.
Bulk resend gap
Trigger: organizer selects many guests and expects EventFarm-style bulk resend.
Resolution: bulk control remains gap-marked until implemented.
Confirmation resend gap
Trigger: organizer tries to resend confirmations to confirmed guests.
Resolution: confirmation resend is exposed as missing parity until the endpoint exists.
Timeframe exclusion gap
Trigger: organizer wants to avoid guests contacted in the last N hours.
Resolution: exclusion UI is a required gap panel until supported.
Suppressed or bounced recipient
Trigger: selected guest is suppressed or hard-bounced.
Resolution: resend is blocked with row-level reason and no outbox row is created.
Stable test attributes
Visibility teeth. Each attribute must be effectively visible when active.
| data-test | Where | Purpose |
|---|---|---|
resend-page | Guest list | Resend-capable surface |
resend-guest-row | Table | Recipient row |
resend-single-cta | Row actions | Single resend |
resend-bulk-bar | Selection state | Bulk resend |
resend-modal | Modal | Confirm resend |
resend-message-type-picker | Modal | Invitation/confirmation |
resend-recipient-preview | Modal | Recipients/exclusions |
resend-send-cta | Modal | Send |
resend-progress | Modal/page | Async progress |
resend-undo-toast | Toast | Undo cancel |
resend-conflict-modal | Modal | Snapshot conflict |
resend-gap-panel | Page | Missing parity |
resend-suppressed-reason | Row | Suppressed/bounced reason |
Agent test plan
- resend-page-renders
- single-resend-invitation
- resend-progress-updates
- permission-denied-boundary
- cross-tenant-404
- soft-delete-audit
- archive-delete-distinction
- publish-edit-lock
- audit-row-every-change
- concurrent-organizers-idempotent
- destructive-undo-window
- bulk-resend-gap
- confirmation-resend-gap
- timeframe-exclusion-gap
- suppressed-recipient-blocked