← All stories

BRANCH · ef-046-edit-guest

Edit guest info, invitations, and responses

EF-046Persona: OrganizerRoots in: event-setup

Organizers edit a guest's contact information, access type, ticket block, counts, notes, custom fields, invitation state, and response. This is marked shippable in the matrix: guest patch updates these values at workers/events/src/routes/guests.ts:938, and registration management supports attendee cancellation at workers/events/src/routes/registrations.ts:1418. The story focuses on preserving auditability and avoiding silent guest-state corruption.

Preconditions

Organizer can view and edit the event guest list; fixture event has invited, confirmed, declined, and cancelled guests.

Happy path / Lifecycle

  1. Open a guest from the guest table.

    The drawer shows identity fields, invitation/response state, access type, ticket count, notes, and custom responses.

  2. Edit contact and response data.

    Save patches only changed fields through the guest route and refreshes the row in place.

  3. Cancel or restore a guest response.

    Destructive response changes are explicit and reversible during the undo window.

Failure modes

Permission denied at the right boundary

Trigger: support/viewer role tries to edit a guest.

Resolution: 403, drawer is read-only or closed, and no guest PII is leaked beyond role scope.

Cross-tenant isolation

Trigger: organizer guesses a guest id from another tenant.

Resolution: 404 response masks existence and omits guest identity.

Soft-delete leaves audit trail

Trigger: organizer removes a guest.

Resolution: guest is soft-deleted/cancelled with audit row containing actor, timestamp, and prior state.

Archive vs delete distinction

Trigger: organizer cancels attendance versus removes the guest record.

Resolution: cancel preserves the invitation/contact record; delete is destructive and separately confirmed.

Edit lock during publish

Trigger: publish/export snapshot starts while guest edit is open.

Resolution: snapshot wins; stale save gets conflict UI and does not change the exported guest state silently.

Audit row on every state change

Trigger: contact, access type, ticket count, note, custom field, invite state, or response changes.

Resolution: audit row records field-level before/after values.

Two organizers concurrent

Trigger: two organizers edit the same guest drawer.

Resolution: version conflict prevents silent overwrite and both see final persisted state after refresh.

Undo window for destructive actions

Trigger: organizer cancels a response or removes a guest.

Resolution: 10 second undo restores the prior status and audit records the restoration.

Capacity revalidation

Trigger: organizer increases a guest's ticket count beyond remaining capacity.

Resolution: save is blocked with a capacity-specific inline error.

Invitation status mismatch

Trigger: organizer changes access type for a guest with an old invite token.

Resolution: old token is invalidated or rebound deterministically, and the drawer shows whether resend is needed.

Stable test attributes

Visibility teeth. Each attribute must be effectively visible when active.

data-testWherePurpose
guest-list-pageRouteGuest list root
guest-rowTableEach guest
guest-edit-drawerDrawerEdit surface
guest-edit-formDrawerPatch form
guest-email-inputFormEmail
guest-response-pickerFormResponse state
guest-ticket-count-stepperFormTicket count
guest-edit-save-ctaFormSave
guest-cancel-response-ctaDrawerCancel response
guest-delete-ctaDrawerRemove guest
guest-edit-undo-toastToastUndo destructive action
guest-edit-conflict-modalModalConflict
guest-edit-validation-errorFormInline validation

Agent test plan

- guest-list-renders
- edit-guest-save
- cancel-response
- 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
- capacity-revalidation
- invitation-status-mismatch