Preconditions
This branch inherits the public event page trunk after the event page resolves. It does not retest trunk-level not-found, archived, draft, capacity-at-page-load, invite-only-without-token, or base Open Graph behavior except where the invitation-reveal flow changes the outcome.
Happy path
Guest opens the public event page and chooses “Find my invitation.”
The reveal panel is an inline form using
ui-formandui-text-input. It asks only for email and keeps event identity visible.Guest submits the email address used by the organizer.
The server performs a normalized, case-insensitive lookup scoped to the event and sends or reveals only that guest's invitation link.
Invite found.
The page shows a neutral success state: “Check your email for your personal invitation link.” If policy allows on-page reveal, the CTA opens the tokenized RSVP path for the matched guest.
Failure modes
Capacity full mid-fill
Trigger: the guest starts lookup while seats remain; another guest takes the last seat before submit.
Lookup still completes, but the invite-link result tells the guest the event just filled and presents the waitlist path. The 409 is not rendered as a broken lookup.
Two-tab idempotency
Trigger: the same guest submits the same email in two tabs.
Both requests share the lookup idempotency semantics; only one reveal audit row and one resend notification are created. The second tab shows the existing reveal outcome.
Network drop during submit
Trigger: the lookup POST lands but the response is lost.
Retry sends the same Idempotency-Key. The guest sees one outcome and receives at most one invitation email for the retry window.
Invalid input rejected without info leak
Trigger: a guessed event slug or fake token is submitted alongside an email.
Known-not-found, malformed, and unknown-token cases return the same generic 4xx envelope, same timing class, and no token-format diagnostics.
Source page archived, draft, or deleted
Trigger: the reveal form is submitted from an old tab after the event is archived, drafted, or deleted.
The flow-specific response is one generic unavailable state. It does not say whether an invitation existed or whether the event was archived versus deleted.
Browser back after success
Trigger: after reveal success, the guest presses browser back.
The email form is restored read-only with the prior result. It does not resubmit or send another invitation email.
Open Graph tags on reveal URL
Trigger: the guest shares the page URL with reveal query state present.
The rendered HTML still has canonical event og:title, og:description, og:image, and og:url; no email or token state appears in the preview.
Bot-fill rate-limited
Trigger: one IP submits ten lookup emails in thirty seconds.
The form shows a friendly retry-after or captcha challenge. The copy does not confirm whether any submitted address was invited.
Email mismatch anti-probing
Trigger: a visitor enters an address that is not invited.
The response shape matches a privacy-preserving success: “If an invitation exists for that email, we’ll send it.” No “not on list” wording, no count of remaining attempts.
Per-email rate limit
Trigger: an attacker rotates IPs while probing one target email.
The server throttles by normalized email + event. UI gives a generic wait message that is identical for invited and uninvited addresses.
Plus-address normalization
Trigger: guest tries alice+event@example.com when the invite was sent to alice@example.com.
Normalization follows the workspace email policy. If aliases are not considered equivalent, the response remains generic and does not reveal the canonical invited address.
Stable test attributes
Visibility teeth. Each attribute must be present and effectively visible when the relevant reveal state is active.
| data-test | Where | Purpose |
|---|---|---|
invite-reveal-panel | Public event page | Root invitation reveal flow |
invite-reveal-form | Inside reveal panel | Email lookup form |
invite-reveal-email | Inside reveal form | Email input |
invite-reveal-submit | Inside reveal form | Submit lookup |
invite-reveal-result | After submit | Generic lookup outcome |
invite-reveal-link-cta | Matched-invite result | Open personal invite when policy permits |
invite-reveal-rate-limit | Rate-limited state | Retry-after or captcha message |
invite-reveal-unavailable | Archived/draft/deleted flow state | Generic unavailable result |
invite-reveal-waitlist-offer | Capacity race state | Waitlist offer after reveal submit |
Agent test plan
Run public-event-page trunk with page-resolves, then exercise the reveal form against matched, unmatched, throttled, unavailable, and race fixtures. All probes use data-test selectors and allowed predicate vocabulary only.