← All stories

BRANCH · ef-063-walkin-add

On-Site Walk-In Add

EF-063 Persona: Event-day staff Stage: Day-of (native) Roots in: native-app-shell

Staff adds a walk-in guest from the native app at the door — fastest path to "registered + checked-in" in one workflow. Captures minimal identity (name, optional email, access type), respects per-access-type capacity, idempotent against rapid double-tap, and writes both registration + check-in audit rows.

Happy path

  1. Staff taps Walk-Ins tab.

    Tab visible only when at least one access type allows walk-ins (per access-type config flag). Otherwise, hidden.

  2. Quick-add form.

    Three fields: name (required), email (optional), access type (required if >1 walk-in-allowed type). Email omitted? No confirmation email sent — they get a printed/verbal handoff.

  3. Submit creates registration + check-in atomically.

    Single endpoint POST that creates registration, marks confirmed, AND records a check-in event. Idempotency-key per form session. Server returns the new guest_id + display name for confirmation.

  4. Confirmation: "Added & checked in" + return to form.

    Auto-clears form after 1.5s for the next walk-in. Pending-sync banner increments if offline.

Failure modes

Walk-ins not allowed for any access type

Trigger: event has all access types with allow_walkins=false.

Walk-Ins tab is hidden entirely. Staff who need to add must contact organizer for config change. Harness: stub event without walk-in-allowed types, tab not visible.

Capacity-full mid-form

Trigger: staff opens form, types name; before submit, last seat goes to another walk-in.

Submit returns 409 SOLD_OUT with banner "Sold out — but you can add to waitlist." Form preserves the typed name + offers waitlist conversion. Harness: stub capacity claim during form-fill, submit, banner visible.

Duplicate-email collision

Trigger: staff enters an email that already has a confirmed registration for this event.

Server returns 409 ALREADY_REGISTERED with "[Name] is already registered. Check in instead?" link to the existing record. Harness: stub existing reg, submit, ALREADY_REGISTERED visible.

Empty-email walk-in

Trigger: staff omits email entirely (paper-attendee scenario).

Allowed. Server stores email=null on the registration. No confirmation email. Audit log row notes "walk-in without email." If two walk-ins later have same name, they're separate registrations — name is not a unique key. Harness: submit with empty email, registration created, no email enqueued.

Offline walk-in

Trigger: device is offline; staff submits walk-in.

Inherits native-app-shell offline contract. Walk-in row created locally with idempotency-key, queued for sync. Confirmation says "Added & queued — will sync when online." Harness: device-state=offline, submit, local row visible, pending-sync banner increments.

Two-staff race on same email

Trigger: two staff both walk-in the same email simultaneously.

Server-side uniqueness on (event_id, email) for confirmed registrations resolves the race — second submit returns 409 ALREADY_REGISTERED. Harness: 2 devices submit same email within 100ms, exactly one 200, one 409.

Permission gate

Trigger: check-in-staff role tries to walk-in (might not have add-guest ability).

Walk-in requires walkins:add ability. Roles that lack it see hidden Walk-Ins tab. Harness: check-in-staff without walkins:add, tab hidden, server POST returns 403.

Audit row creates both registration + check-in

Trigger: walk-in succeeds.

Audit log gets two rows: registration_created + check_in, both timestamped, same actor + same scan_ts. Harness: walk-in submit, server audit log has both rows.

Auto-clear timer interrupted by next-tap

Trigger: staff taps "Add another" before the 1.5s auto-clear timer.

Tap cancels the timer immediately, clears the form, refocuses name input. No flicker. Harness: simulate rapid tap, no double-render.

Cross-tenant walk-in attempt

Trigger: forged event_id in walk-in submit (tenant attempting cross-tenant write).

Server returns 404. Harness: forge event_id, submit, server 404.

Stable test attributes

walkin-tabTab navVisible only when access types allow walk-ins
walkin-formTab contentQuick-add form
walkin-name-inputFormRequired field
walkin-email-inputFormOptional field
walkin-access-type-pickerFormRequired if >1 walk-in-allowed type
walkin-submitFormCreates registration + check-in atomically
walkin-confirmationPost-submit"Added & checked in"
walkin-sold-out-banner409 SOLD_OUT+ waitlist conversion offer
walkin-already-registered409 ALREADY_REGISTERED+ "check in instead" link

Agent test plan

Probe list
- (manual) walkin-tab-hidden-when-disabled: stub event without walkin-allowed types, tab not visible
- (manual) capacity-full-mid-form: 409 SOLD_OUT + waitlist offer
- (manual) duplicate-email-409: existing reg, ALREADY_REGISTERED with check-in link
- (manual) empty-email-allowed: submit without email, registration created no email enqueued
- (manual) offline-walkin-queues: device offline, local row created, pending banner increments
- (manual) two-staff-race-resolved: 2 devices same email, exactly one 200
- (manual) permission-gate: role without walkins:add, tab hidden + 403
- audit-log-both-rows: walk-in success, registration_created + check_in audit rows
- (manual) auto-clear-cancellable: rapid tap, no flicker
- cross-tenant-404: forged event_id, 404