← All stories

BRANCH · ef-048-arrival-alerts

Arrival Alerts

EF-048Persona: OrganizerRoots in: event-setup

Arrival Alerts are partial: arrival_alert_json is accepted on guest create/patch at workers/events/src/routes/guests.ts:49 and :965, but check-in dispatch is not implemented end-to-end. This branch captures storage, per-guest config, recipients, opt-out, and the dispatch gap.

Preconditions

Organizer can edit a guest; fixture includes staff check-in event that can simulate arrival without sending real notifications.

Happy path / Lifecycle

  1. Open a guest arrival alert editor.

    Organizer enables alert and enters one or more email/text recipients.

  2. Save per-guest config.

    Config persists in arrival_alert_json and survives unrelated guest edits.

  3. Simulate check-in.

    The branch currently expects a visible dispatch gap panel until notification dispatch is wired.

Failure modes

Permission denied at the right boundary

Trigger: viewer/support attempts organizer-only operation.

Resolution: the write request returns 403, the editable surface remains closed or read-only, and the response does not leak hidden guest, event, or tenant fields.

Cross-tenant isolation

Trigger: tenant-A user guesses tenant-B resource id.

Resolution: the server returns 404 instead of 403, masks existence, and the UI renders a generic not-found state.

Soft-delete leaves audit trail

Trigger: organizer removes or deactivates the configured object.

Resolution: the row is marked inactive/deleted with actor, timestamp, and prior state preserved in audit.

Archive vs delete distinction

Trigger: organizer chooses between reversible archive and destructive delete.

Resolution: archive stays reversible and copy/export labels it archived; delete requires separate destructive confirmation and changes copy behavior.

Edit lock during publish

Trigger: publish snapshot begins while an edit is open.

Resolution: publish wins; stale save receives a deterministic conflict modal and does not mutate the published snapshot silently.

Audit log row written on every state change

Trigger: organizer saves any state transition.

Resolution: each state mutation writes an audit row with actor, timestamp, entity id, and before/after payload.

Two organizers concurrent

Trigger: two organizers edit the same state from stale versions.

Resolution: the second save gets conflict UI, both sessions refresh to the same final state, and there is no silent overwrite.

Undo window for destructive actions

Trigger: organizer deletes, cancels, or clears the object.

Resolution: a visible undo affordance lasts 10 seconds and restores the exact prior state when used.

Parity gap: alert dispatch deferred

Trigger: matrix says data is stored but check-in dispatch is not implemented end-to-end.

Resolution: visible gap panel remains until check-in triggers email/text dispatch and closes the parity gap.

Per-guest alert config preserved across edits

Trigger: organizer edits unrelated guest fields after setting arrival alert.

Resolution: arrival_alert_json remains unchanged unless the alert form itself is saved.

Multi-recipient alert

Trigger: organizer adds multiple email/text recipients.

Resolution: validation accepts multiple recipients, stores channel and destination per recipient, and shows all recipients after reload.

Opt-out of alert per guest

Trigger: organizer disables arrival alert for a specific guest.

Resolution: config remains explicit opt-out, no dispatch is attempted for that guest, and audit records the change.

Stable test attributes

Visibility teeth. Each attribute must be effectively visible when active and must match the agent probes.

data-testWherePurpose
arrival-alerts-drawersurfacedrawer
arrival-alerts-formsurfaceform
arrival-alerts-enabled-togglesurfaceenabled toggle
arrival-alerts-recipient-listsurfacerecipient list
arrival-alerts-recipient-inputsurfacerecipient input
arrival-alerts-channel-pickersurfacechannel picker
arrival-alerts-save-ctasurfacesave cta
arrival-alerts-archive-alert-ctasurfacearchive alert cta
arrival-alerts-delete-alert-ctasurfacedelete alert cta
arrival-alerts-undo-toastsurfaceundo toast
arrival-alerts-conflict-modalsurfaceconflict modal
arrival-alerts-validation-errorsurfacevalidation error
arrival-alerts-gap-panelsurfacegap panel

Agent test plan

- arrival-alerts-opens
- arrival-alerts-saves
- arrival-alerts-audit-visible
- permission-denied-boundary
- cross-tenant-404
- soft-delete-audit
- archive-delete-distinction
- publish-edit-lock
- audit-row-every-change
- concurrent-organizers-conflict
- destructive-undo-window
- dispatch-gap-probe
- per-guest-config-preserved
- multi-recipient-alert
- per-guest-opt-out
- evaluate-arrival-alerts