← All stories

BRANCH · ef-044-group-invite-and-upload

Group invite and upload invite

EF-044Persona: OrganizerRoots in: event-setup

Group invite and upload is partial in the matrix. The route begins at workers/events/src/routes/guests.ts:544, import preview at :631, and commit at :662. This branch requires preview, partial success, full-file commits, row isolation, and async invite progress.

Preconditions

Organizer can import guests; fixture includes valid rows, invalid rows, duplicates, and a large file over the preview size.

Happy path / Lifecycle

  1. Open Group invite/upload.

    Organizer uploads CSV/XLSX and sees a parsed preview before any rows commit.

  2. Resolve mappings and commit.

    Valid rows persist, invalid rows remain downloadable with reasons, and audit records counts.

  3. Send invites as an async job.

    Bulk send progress shows sent, failed, and retryable counts.

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.

CSV import preview component used

Trigger: organizer uploads a guest CSV.

Resolution: upload parses into ui-csv-import-preview with valid, warning, and rejected row groups before commit.

Partial-success commit

Trigger: some rows are valid and some fail validation.

Resolution: commit persists valid rows, reports failed rows with reasons, and records partial-success in audit.

Large-file commit processes all rows, not preview only

Trigger: upload has more rows than the preview page displays.

Resolution: commit job processes the full uploaded file, not just visible preview rows.

Bulk resend progress

Trigger: organizer sends invitations after group import.

Resolution: ui-async-job-tracker displays batch progress, failures, and retry affordance.

Cross-tenant rejection at row level

Trigger: CSV includes contact/event ids from another tenant.

Resolution: those rows are rejected as not found, valid same-tenant rows can still commit, and no foreign ids leak.

Parity gap: upload breadth incomplete

Trigger: matrix marks group invite/upload partial.

Resolution: gap panel stays visible until duplicate reports and bulk email timing controls reach EventFarm parity.

Stable test attributes

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

data-testWherePurpose
group-invite-upload-pagesurfacepage
group-invite-upload-file-uploadersurfacefile uploader
group-invite-upload-csv-previewsurfacecsv preview
group-invite-upload-commit-ctasurfacecommit cta
group-invite-upload-result-summarysurfaceresult summary
group-invite-upload-job-trackersurfacejob tracker
group-invite-upload-row-errorsurfacerow error
group-invite-upload-archive-batch-ctasurfacearchive batch cta
group-invite-upload-delete-batch-ctasurfacedelete batch cta
group-invite-upload-undo-toastsurfaceundo toast
group-invite-upload-conflict-modalsurfaceconflict modal
group-invite-upload-gap-panelsurfacegap panel

Agent test plan

- group-invite-and-upload-opens
- group-invite-and-upload-saves
- group-invite-and-upload-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
- csv-import-preview-required
- partial-success-commit
- large-file-commit-all-rows
- bulk-resend-progress
- row-level-cross-tenant-rejection
- group-upload-gap-probe
- evaluate-group-invite-and-upload