Preconditions
Organizer can edit a guest; fixture includes staff check-in event that can simulate arrival without sending real notifications.
Happy path / Lifecycle
Open a guest arrival alert editor.
Organizer enables alert and enters one or more email/text recipients.
Save per-guest config.
Config persists in arrival_alert_json and survives unrelated guest edits.
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-test | Where | Purpose |
|---|---|---|
arrival-alerts-drawer | surface | drawer |
arrival-alerts-form | surface | form |
arrival-alerts-enabled-toggle | surface | enabled toggle |
arrival-alerts-recipient-list | surface | recipient list |
arrival-alerts-recipient-input | surface | recipient input |
arrival-alerts-channel-picker | surface | channel picker |
arrival-alerts-save-cta | surface | save cta |
arrival-alerts-archive-alert-cta | surface | archive alert cta |
arrival-alerts-delete-alert-cta | surface | delete alert cta |
arrival-alerts-undo-toast | surface | undo toast |
arrival-alerts-conflict-modal | surface | conflict modal |
arrival-alerts-validation-error | surface | validation error |
arrival-alerts-gap-panel | surface | gap 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