Restaurant curbside pickup software

CurbPing

Perfectly timed curbside

CurbPing is a lightweight curbside pickup platform that auto-detects arrivals via a browser 'I'm here' link and tells staff when and where customers arrive—no apps or hardware. Built for SMS-first independent restaurant operators, it ends phone-tag and misparked cars, assigns numbered bays, and keeps food hot while cutting waits 42% and freeing a staff hour per 50 orders.

Subscribe to get amazing product ideas like this one delivered daily to your inbox!

CurbPing

Product Details

Explore this AI-generated product idea in detail. Each aspect has been thoughtfully created to inspire your next venture.

Vision & Mission

Vision
Empower every neighborhood restaurant to deliver effortless, perfectly timed curbside moments that delight guests and free teams for hospitality.
Long Term Goal
Within 4 years, power curbside pickup for 20,000 independent restaurants, achieving sub‑2‑minute handoffs, 95% on‑time arrivals, 28% fewer abandoned pickups, and saving 10 million staff hours annually.
Impact
For independent restaurants, CurbPing cuts curbside waits by 42%, reduces abandoned pickups 28%, and frees one staff hour per shift for every 50 orders, yielding hotter food, 35% fewer ‘where are you?’ calls, and 20% higher tips by ensuring staff meet the right car on time.

Problem & Solution

Problem Statement
Independent restaurant owners and managers can’t predict curbside arrivals, misplace cars, and waste staff on phone-tag, leading to cold food and frustrated guests. Existing curbside tools are bulky, expensive, and require apps or hardware that SMS-first operators won’t adopt.
Solution Overview
CurbPing replaces curbside guesswork with a browser “I’m Here” link that auto-detects arrivals and assigns a numbered bay, so staff meet the right car on time. Automatic check-in and two-way SMS for substitutions or delays end phone-tag and misparked cars, keeping food hot and lines moving.

Details & Audience

Description
CurbPing is a lightweight curbside pickup platform with automatic arrival detection that tells staff when and where customers arrive. Built for independent restaurant owners and managers who want faster, calmer handoffs. It ends phone-tag, misparked cars, and cold food, cutting curbside waits by 42% and freeing a staff hour per 50 orders. A browser-based "I'm Here" geofence link auto-checks guests in and assigns a numbered bay, no apps or hardware.
Target Audience
Independent restaurant owners and managers (25-55), SMS-first, app-averse operators needing predictable arrivals and faster handoffs.
Inspiration
Rain hammered my windshield outside a taco shop. I balanced a collapsing paper bag, texted the Sharpie sign—Text HERE—and watched taillights stack while my message vanished into the void. Inside, a cook scanned the lot, phone ringing, tacos cooling. In that jam, the fix snapped into place: a simple browser link that auto-detects arrival and assigns a bay—no apps, no hardware—so the right car meets the right hands on time.

User Personas

Detailed profiles of the target users who would benefit most from this product.

U

Urban Bay Maximizer Marco

- 32–45, General Manager at independent bistro - Dense urban core, 2–4 curb bays, strict enforcement - 8–12 years FOH/ops experience - 80–120 curbside orders on peak shifts - Compensation $60–85k plus bonus

Background

Started as host wrangling double-parkers; learned hard lessons from ticket-happy officers. Promoted to GM after cutting curb chaos with cones and laminated maps; now seeks software precision to keep food hot and curb clear.

Needs & Pain Points

Needs

1. Real-time bay occupancy with automatic reassignment 2. Clear car identification without app downloads 3. Dwell and wait reports by hour

Pain Points

1. Double-parking tickets during dinner rush 2. Guests parking in the wrong alley 3. Runners circling blocks with cooling bags

Psychographics

- Hates idle curb minutes, worships throughput - Pragmatic, data-first persuader of owners - Calm under pressure, drills crisp SOPs - Protects guest dignity over noisy theatrics

Channels

1. LinkedIn industry groups 2. Nation's Restaurant News website 3. RestaurantOwner.com forum 4. YouTube operations tips 5. Toast Community forum

G

Ghost Kitchen Dispatcher Gia

- 26–38, dispatcher/expediter in commissary kitchen - Suburban industrial park, shared parking lot - Manages 150–250 pickups nightly across brands - Hourly $20–28 with shift-lead duties - Coordinates guests and third-party drivers

Background

Ex-line cook turned dispatcher during the virtual-brand boom. After losing orders to crowded hallways and confused drivers, built color-coded clipboards; now upgrading to unified, SMS-first coordination.

Needs & Pain Points

Needs

1. Multi-brand labels on a single arrivals board 2. Driver and guest-friendly SMS templates 3. Instant reroute to alternate doors or lots

Pain Points

1. Hallways jammed with waiting drivers 2. Lost drivers in sprawling complexes 3. Orders mixed between sibling brands

Psychographics

- Thrives on taming organized chaos - Brand-agnostic, fiercely punctual - Prefers simple, resilient tools over suites - Empathetic to overworked gig drivers

Channels

1. Facebook Groups ghost kitchens 2. YouTube ghost kitchen ops 3. Reddit r/ghostkitchens 4. LinkedIn commissary networks 5. DoorDash driver forum

T

Training Turnkey Talia

- 28–42, training manager for 4–12 locations - Hybrid schedule: stores 60%, remote 40% - Builds SOPs in LMS (Trainual/Lessonly) - Oversees openings and retrains underperformers - Salary $65–90k

Background

Former shift lead promoted after opening two stores in one quarter. Burned by tool sprawl and inconsistent greetings; now champions simple scripts backed by data and role-based presets.

Needs & Pain Points

Needs

1. Printable SOPs with annotated screenshots 2. Role-based permissions and quick-start presets 3. Adoption tracking across locations

Pain Points

1. New hires freezing during rush scripts 2. Conflicting instructions across shifts 3. Password lockouts on shared devices

Psychographics

- One screen, one script evangelist - Data-informed, story-driven coach - Values repeatability over heroics - Seeks frictionless tech for rookies

Channels

1. LinkedIn learning leaders 2. Trainual community 3. YouTube how-tos 4. Restaurant Dive newsletter 5. Slack vendor channels

M

Marketing Message Maestro Mina

- 29–40, solo marketer at indie group - Manages SMS, email, socials across 3–6 stores - Suburban, mid-market audiences - Works cross-functionally with ops weekly - Salary $55–80k

Background

Started in social, grew into full-stack local marketing. Learned pickup friction kills loyalty; now connects speed metrics to messaging and promotions to prove revenue lift.

Needs & Pain Points

Needs

1. Editable SMS templates with merge tags 2. CSAT surveys triggered post-pickup 3. Exportable wait data for campaigns

Pain Points

1. Rigid POS-controlled SMS copy 2. Tone drift across locations 3. No loop from speed to loyalty

Psychographics

- Obsessed with measurable message performance - Guards consistent brand tone everywhere - Pragmatic experimenter shipping small tests - Customer-obsessed and loyalty-driven

Channels

1. Klaviyo community 2. LinkedIn local marketers 3. Facebook groups restaurateurs 4. Mailchimp resources 5. YouTube A/B testing tips

E

Event-Rush Orchestrator Owen

- 34–50, GM/owner near arena or campus - 200–400 curbside orders on event days - Shared lots with neighboring shops - Coordinates with city and security - Income $80–120k plus event bonuses

Background

Former concessions manager fluent in surge logistics. Opened near a stadium and suffered pregame gridlock; now designs dynamic curb maps and time-based rules to survive crush periods.

Needs & Pain Points

Needs

1. Dynamic bay counts by time block 2. Pre-scheduled slots with auto-texts 3. Overflow lot routing during peak

Pain Points

1. Gridlock spilling into neighbor storefronts 2. Guests trapped behind barricades 3. Runners stranded across blocked streets

Psychographics

- Surge strategist, plans like a general - Data hawk during live events - Prefers flexible tools over fixed setups - Community-minded, avoids neighbor conflicts

Channels

1. X local updates 2. Waze ads 3. LinkedIn hospitality ops 4. Eventbrite local calendar 5. YouTube traffic control tips

A

Accessibility-First Alana

- 30–48, assistant GM or service director - Suburban casual-dining with large takeout mix - Trains staff on ADA-sensitive service - Partners with local disability advocates - Salary $55–75k

Background

Caregiver for a parent with limited mobility; saw curbside blind spots firsthand. Instituted priority bays and trunk-drop options; now seeks tooling to flag needs and simplify arrivals.

Needs & Pain Points

Needs

1. Priority flags for accessibility notes 2. Step-free, crystal-clear arrival instructions 3. Trunk-drop confirmation options

Pain Points

1. Guests forced to call from cars 2. Confusing signage causing missed bays 3. Hot items cooling during delays

Psychographics

- Empathy-led and rigorously detail-oriented - Dignity over speed when necessary - Seeks clarity and predictability always - Favors tools reducing cognitive load

Channels

1. Facebook local community 2. Yelp business owner tools 3. LinkedIn hospitality service 4. YouTube accessibility training 5. ADA.gov updates

Product Features

Key capabilities that make this product valuable to its target users.

One-Tap Wallets

Guests pay in seconds using Apple Pay, Google Pay, or saved cards inside the secure SMS link—no app, no typing. Faster checkout shortens curbside dwell, cuts phone‑tag, and frees staff to run orders instead of running cards.

Requirements

Secure SMS Payment Session
"As a guest receiving a curbside SMS, I want a secure, one‑tap payment session that auto‑detects my device wallet so that I can pay in seconds without installing an app or typing card details."
Description

Generate and deliver a short‑lived, signed payment session via the curbside SMS link that opens a mobile web checkout optimized for one‑tap wallets. The session must bind to the order, phone number, and bay assignment, auto‑detect device capabilities (Apple Pay, Google Pay, saved card), and present the fastest eligible option by default. Enforce anti‑replay, CSRF protection, and rate limiting; expire sessions after configurable TTL; and support safe reissue with idempotency keys. Provide graceful fallbacks (manual card via hosted fields) and clear error states. Capture analytics for conversion and dwell time without storing PAN or sensitive wallet data.

Acceptance Criteria
SMS Link Generates Short‑Lived Signed Checkout Session
Given an unpaid order with id X, phone number N, and bay assignment B And an SMS link is issued to N When the recipient opens the link within the configured TTL Then the backend creates a signed payment session token with claims {orderId=X, phoneHash=hash(N), bayId=B, exp<=now+TTL, nonce} And the checkout loads using that session and displays the correct order total and bay B And the session is invalid if the order is already paid And P95 time to first interactive render is <= 2.0 seconds on 4G
Device Capability Detection and Fastest Eligible Wallet Default
Given a payment session loads on a device When the checkout determines wallet eligibility Then if Apple Pay is eligible and merchant domain is verified, Apple Pay is shown as the primary, preselected CTA And else if Google Pay is eligible and merchant config is valid, Google Pay is shown as the primary, preselected CTA And else if a saved card token exists and is valid, the saved card is shown as primary And else a PCI-compliant manual card form (hosted fields) is shown as fallback And only eligible methods are displayed; ineligible methods are hidden or disabled with a tooltip
Anti‑Replay, CSRF, and Rate Limiting Enforcement
Given a valid session token with nonce N When a payment confirm request is received Then the backend validates signature, exp, nonce uniqueness, origin, and CSRF token before creating any payment intent And if the token is expired, reused, tampered, or the CSRF token is missing/invalid, the request is rejected with 401/403 and no payment intent is created And payment confirm attempts are rate limited to <= 5 per minute per (phoneHash + IP + orderId) with HTTP 429 on excess And user-facing errors are generic and safe, while a technical error code is included for support
Session Expiry and Idempotent Reissue
Given a session TTL is configured (e.g., 15 minutes) When a guest opens an expired payment link Then the UI shows "Link expired" and offers to resend a new secure link When a new link is requested Then a new session is created with a fresh token and the same idempotency key for orderId such that repeated submissions create at most one charge/payment intent And the latest active session supersedes prior sessions, which are invalidated And reopens within 5 minutes reuse the most recent active session where possible
Graceful Fallback and Clear Error States
Given a wallet payment initiation fails (user cancellation, network error, or wallet unavailable) When the failure is detected Then the UI displays a clear, actionable message and shows a PCI-compliant manual card form (hosted fields) without exposing PAN to our servers And if a saved card token is available and valid, it is offered as a selectable option And the user can retry the wallet once; subsequent attempts default to the fallback form And the order remains in a payable state until TTL expires
Privacy‑Safe Conversion and Dwell Analytics
Given a payment session lifecycle from open to completion When key milestones occur Then the system records events: session_started, wallets_detected, default_method, wallet_button_tapped, payment_authorized or payment_failed, time_from_link_open_to_payment, and time_from "I'm here" to payment And no PAN, CVV, or raw wallet tokens are stored; only last4 and brand from the processor may be recorded, and phone numbers are stored as salted hashes And analytics are queryable by store and day for conversion rate and median dwell, with access controls and audit logging enforced
Order State Update and Bay Visibility on Payment
Given a successful payment authorization or capture for order X bound to bay B When the processor callback/webhook is received Then order X transitions to Paid while retaining bayId=B And the staff console reflects Paid status and bay B within 3 seconds P95 And the guest confirmation screen displays pickup instructions without exposing sensitive payment data And duplicate callbacks do not create duplicate state changes due to idempotency
Apple Pay Integration
"As an iPhone user, I want to pay with Apple Pay from the SMS link so that checkout finishes quickly while I’m arriving."
Description

Implement Apple Pay on the web for iOS/macOS Safari using Payment Request API with merchant validation, Apple Merchant ID, and domain association. Populate the sheet with merchant name, line items, tax, tip, discounts, and final total; support dynamic updates when tip or fees change. Handle SCA/3DS challenges, declines, and cancellation paths with clear messaging. On success, tokenize the network token via the PSP, finalize the order, and return a confirmation state that drives staff notifications and receipts. Provide fallbacks to saved card or manual entry when Apple Pay is unavailable.

Acceptance Criteria
Apple Pay Eligibility and Display in Safari
Given a guest opens the secure SMS checkout link in Safari on iOS 15+ or macOS 12+ with Apple Pay set up, When the checkout loads, Then the Apple Pay button is visible and enabled within 500 ms. Given a guest opens the link in an unsupported browser or Apple Pay is not set up, When the checkout loads, Then the Apple Pay option is hidden (or disabled with explanatory text) and saved card and manual entry options are shown within 500 ms. Given device/browser eligibility changes during the session, When eligibility is rechecked, Then Apple Pay availability updates without a page reload.
Merchant Validation and Domain Association
Given the guest taps Apple Pay, When merchant validation runs, Then a valid merchantSession is obtained from Apple using the configured Apple Merchant ID and associated domain and the Apple Pay sheet opens within 2 seconds. Given merchant validation fails, When one automatic retry also fails, Then a clear non-technical error is displayed and Apple Pay is disabled for this session while fallback methods remain available.
Accurate Line Items, Taxes, Discounts, Tips, and Totals
Given the order has items, tax, fees, tip selection, and discounts, When the Apple Pay sheet opens, Then the merchant name and line items reflect the current cart and the total equals items + tax + fees + tip − discounts to the cent. Given the guest changes tip or fees on checkout, When the update is triggered, Then the Apple Pay sheet total updates via Payment Request API within 500 ms and matches the backend recomputation. Given the guest enters a custom tip, When the value is invalid, Then an inline validation error is shown and the Apple Pay sheet is not opened/updated; When the value is valid, Then the sheet reflects the new total.
SCA/3DS Challenges and Authorization Flow
Given the issuer requires SCA/3DS, When the guest authorizes with Face ID/Touch ID/passcode, Then the challenge completes and authorization proceeds without losing cart context. Given the SCA/3DS challenge fails or times out, When the flow ends, Then a clear error message is shown within 2 seconds and options to retry Apple Pay or use another payment method are presented.
Declines and Customer Cancellations Handling
Given the issuer declines after Apple Pay authorization, When the PSP returns a decline, Then the guest sees a non-sensitive decline message within 2 seconds and can retry Apple Pay, select a saved card, or enter card details; No order is finalized and no receipt is sent. Given the guest cancels the Apple Pay sheet, When the sheet is dismissed, Then the checkout remains on the same step, Apple Pay stays available (if supported), and no charge or order is created; the event is logged with reason "canceled".
Tokenization, Capture, Confirmation, and Notifications
Given a successful Apple Pay authorization, When the payment token is received, Then PSP tokenization succeeds and a single charge is created following capture rules with idempotency to prevent duplicates. Then the order state transitions to Paid, a unique orderId is persisted, staff notification is emitted within 3 seconds, and the guest receives a receipt (SMS and/or email) including bay assignment details. Given duplicate PSP webhooks or client retries, When they arrive, Then no duplicate charges or orders are created.
Fallback to Saved Card and Manual Entry
Given Apple Pay is unavailable or disabled, When the checkout loads, Then saved card options (if any) and manual card entry are presented as primary methods. Given the guest selects a saved card, When authentication (including 3DS if required) completes, Then the payment succeeds and the same confirmation state, notifications, and receipts are produced as with Apple Pay. Given manual entry is selected, When the guest submits valid card details, Then the payment succeeds with the same post-payment outcomes; When invalid, Then inline validation prevents submission.
Google Pay Integration
"As an Android user, I want to use Google Pay directly from the SMS link so that I can pay without typing card info."
Description

Enable Google Pay for Android/Chrome with gateway tokenization through the PSP, including merchant ID configuration, allowed card networks, auth methods, and country/currency settings. Present the Google Pay sheet with order summary, tax, tip, and discounts; support dynamic price updates and SCA where required. Handle success, decline, and cancel paths consistently; on success, confirm payment, close the sheet, and progress the order flow. Provide fallbacks to saved card or hosted fields if Google Pay is unavailable or ineligible.

Acceptance Criteria
Google Pay Availability & Eligibility Gatekeeping
Given an Android device or Chrome browser opens the secure SMS checkout link and Google Pay is available with at least one eligible card for the merchant’s country/currency When the payment options are rendered Then the Google Pay button is displayed as the primary option and fallback payment methods are available under an alternate selector Given Google Pay is unavailable on the device or the user has no eligible cards When the payment options are rendered Then Google Pay is not shown and saved card or hosted fields are displayed as the primary option Given the merchant configuration or order currency/country is unsupported for Google Pay When the payment options are rendered Then Google Pay is suppressed and the ineligible reason is logged for telemetry
Gateway Tokenization & Merchant Configuration
Given the environment is TEST When requesting a Google Pay token Then the request uses the configured test merchantId, gateway, gatewayMerchantId, allowedAuthMethods ["PAN_ONLY","CRYPTOGRAM_3DS"], and allowedCardNetworks per merchant settings Given the environment is PRODUCTION When requesting a Google Pay token Then the production merchantId and gatewayMerchantId are used and test cards are rejected Given a valid PaymentData is returned When it is sent to the PSP Then the PSP accepts the tokenized payload and returns an authorization or capture response within 5 seconds Given tokenization or configuration is invalid When a tokenization error occurs Then a friendly error is shown, fallback methods remain available, and the error is logged with a correlation ID
Google Pay Sheet Presentation & Order Summary
Given an unpaid order with items, tax, tip, and discounts When the Google Pay sheet is opened Then merchantName, countryCode, currencyCode, and totalPrice match backend-calculated amounts within ±$0.01 (or minor unit equivalent) Given a tip amount is preselected When the sheet loads Then the displayed total includes the tip, tax, and discounts as provided by the backend Given curbside pickup requires no shipping When the sheet loads Then no shipping address or shipping options are requested
Dynamic Price Updates & SCA
Given the user changes the tip or a discount is applied before confirmation When transactionInfo is updated Then the Google Pay sheet total updates to the new amount prior to authorization Given the issuer requires Strong Customer Authentication When the user confirms in the Google Pay sheet Then a challenge flow is presented and upon successful completion a PaymentData token is returned Given SCA fails or times out When the PSP responds (or no response is received) Then the payment is not captured/authorized, an error is shown, and the user can retry or choose a fallback method
Success Path — Confirm, Close, and Progress Order
Given the PSP returns a successful authorization or capture for the Google Pay token When CurbPing processes the response Then the Google Pay sheet closes within 2 seconds, the order is marked paid, a confirmation is displayed, and the flow advances to bay assignment Given duplicate callbacks or retries occur When processing the same payment token Then idempotency prevents duplicate charges and the order remains in a single paid state Given webhooks are delayed When a synchronous success has been shown Then the order status remains consistent and is reconciled on webhook receipt without double-processing
Decline Handling — Consistent Errors and Retry
Given the PSP declines the transaction or returns a soft error When handling the response Then a clear retryable error is shown, the sheet is closed, the order remains unpaid, and the user can retry Google Pay or select a fallback method Given a hard error or irrecoverable tokenization failure occurs When handling the error Then a clear non-retryable error is shown and only fallback payment options are presented
Cancel Handling & Timeout
Given the user dismisses the Google Pay sheet or presses cancel When control returns to CurbPing Then no authorization attempt is made, the order remains unpaid, and the user is returned to payment options with Google Pay still available Given no response is received from Google Pay or the PSP within 15 seconds When the timeout threshold is reached Then the flow aborts gracefully, the order remains unpaid, an error is shown, and retry/fallback options are available
Saved Card Vaulting
"As a repeat guest, I want to reuse a saved card securely so that I can pay even if my device wallet isn’t available."
Description

Offer secure saved card payments using PSP tokenization so returning guests can pay with one tap even without a device wallet. Present masked card details (brand, last 4), allow add/remove with explicit consent, and bind tokens to the guest’s phone number and merchant. Use hosted fields/PSP UIs to keep PAN off CurbPing systems and store only non‑sensitive metadata. Respect token lifecycle (expiry, invalidation) and provide fallbacks to wallets or manual entry if a token fails. Log card management events for auditability.

Acceptance Criteria
First-Time Save-Card via Hosted Fields
Given a guest opens a secure SMS payment link for merchant M tied to phone number P And the checkout uses PSP-hosted fields or PSP UI so PAN/CVV never load from CurbPing domains When the guest enters card details and explicitly checks an unchecked “Save card for future curbside payments” box And confirms payment Then CurbPing receives only a PSP token and non-sensitive metadata (brand, last4, expiry, token_id) And CurbPing stores the token bound to P and M with consent capture (timestamp, consent copy version, IP/user-agent) And no PAN/CVV is sent to or stored on CurbPing servers, logs, or databases (verified by request/response inspection) And the confirmation indicates the card is saved using masked brand and last4
Returning Guest One-Tap with Saved Token
Given a guest with at least one active saved token bound to phone number P for merchant M opens the SMS payment link When the guest selects a saved card and taps Pay Then the charge is created using the PSP token without re-entering PAN/CVV And only tokens scoped to P and M are displayed and eligible And the receipt shows masked brand and last4 And the payment completes without additional input unless 3DS/SCA is required by the PSP
Masked Card Details and Selection UI
Given saved cards exist for phone number P and merchant M When the payment method list is rendered Then each saved card displays brand, last4, and expiry (MM/YY) only—never full PAN or CVV And the most recently used saved card is preselected And the UI also shows options to “Pay with Apple Pay/Google Pay” when available and “Use a different card” And if no saved cards exist, the saved card section is not shown
Token Scope Enforcement (Phone Number + Merchant)
Given tokens are stored bound to phone number P and merchant M When a guest with phone number P1 opens a link for merchant M or a guest with phone number P opens a link for merchant M2 Then tokens not matching both P and M are not listed and cannot be used for authorization And any attempt to use a mismatched token is rejected before reaching the PSP and is logged as a security event And the UI prompts the guest to add a new card or use a device wallet
Token Lifecycle and Update Flow
Given a saved token is expired, revoked, or otherwise invalid per PSP When the guest attempts to pay with that token Then the attempt is prevented and an inline message explains the card needs update or a different method And the token is marked inactive and hidden from future lists And the guest is offered to update card via hosted fields; upon successful update the old token is replaced with a new token bound to the same P and M with refreshed metadata and consent linkage And if PSP returns a soft-decline requiring re-authentication (e.g., 3DS), the flow routes through the PSP challenge and resumes to completion
Add/Remove Saved Cards Management
Given a guest opens the Manage Payment Methods view from the SMS link for merchant M When the guest taps Remove on a saved card and confirms Then CurbPing removes the token reference, requests token deletion/inactivation from the PSP when supported, and the card disappears from the list immediately And the removal event is persisted and reflected in subsequent sessions And when the guest adds a new card via hosted fields and checks consent, a new token is created and bound to P and M and appears in the list with masked details
Fallback to Wallet or Manual Entry on Token Failure
Given a token charge attempt fails due to token invalidation, gateway error, or PSP unavailability When the failure is detected Then the UI presents fallback options: Apple Pay/Google Pay (when supported) and “Pay with a different card” via hosted fields And selecting a fallback method allows the guest to complete payment in the same session And the failure and chosen fallback path are logged without storing PAN/CVV
Audit Logging for Card and Token Events
Given the system processes card save, update, delete, pay-with-token, and token-failure events When any such event occurs Then an immutable audit record is written with fields: timestamp (UTC), merchant_id, phone_hash, token_id (masked), action, outcome, error_code/reason (if any), actor_channel (guest SMS), and request_id And no PAN, CVV, or full billing address is logged And authorized admins can query these records by merchant_id and date range
Real‑time Payment State & Order Sync
"As a curbside runner, I want the order to flip to Paid automatically on my screen so that I know I can hand off without running a card at the car."
Description

Propagate payment events (authorized, captured, failed, refunded) to the order timeline via signed webhooks and an internal event bus with idempotency. Flip orders to Paid in the staff console/KDS instantly, show method (Apple Pay, Google Pay, saved card last 4), and trigger pick‑run workflows and receipts. Persist a reliable audit trail with timestamps and PSP transaction IDs. Provide resilient retry/backoff on webhook failures and surface reconciliation tooling for mismatches.

Acceptance Criteria
Instant Paid Flip on Successful Capture
Given an order in Pending Payment and a valid PSP capture event is received via signed webhook When the webhook is verified and processed through the event bus Then the order status in the Staff Console and KDS updates to Paid within 2 seconds And the displayed payment method shows "Apple Pay", "Google Pay", or "Card •••• {last4}" as provided by the PSP And a pick-run workflow is triggered within 2 seconds of the Paid state And a receipt is sent to the guest via the existing SMS link within 5 seconds And the order timeline records a Payment Captured entry with ISO-8601 timestamp and PSP transaction ID And duplicate delivery of the same event does not create additional timeline entries or re-trigger workflows
Authorization-Only Logged Without State Flip
Given a PSP authorization event (not captured) is received and verified When the event is processed Then the order status remains Not Paid And no pick-run workflow or receipt is triggered And the order timeline records a Payment Authorized entry with ISO-8601 timestamp and PSP authorization ID within 2 seconds And when a subsequent capture event arrives, the order flips to Paid per the capture criteria without duplicate authorization entries
Failed Payment Handling and Staff Alert
Given a PSP payment failed/declined event is received and verified When the event is processed Then the order status remains Not Paid And the Staff Console surfaces a visible non-blocking banner with the failure reason code/message within 2 seconds And no pick-run workflow or receipt is triggered And the order timeline records a Payment Failed entry with ISO-8601 timestamp, PSP transaction/attempt ID, and reason code
Refund Event Propagation and Partial Support
Given a full or partial refund event is received and verified for a previously captured transaction When the event is processed Then the order timeline records a Refund entry with amount, currency, ISO-8601 timestamp, and PSP refund ID within 2 seconds And the Staff Console shows a Refunded badge for full refunds or a Partially Refunded indicator with remaining net amount And a refund receipt is sent to the guest via SMS within 5 seconds And the audit trail preserves immutable entries for capture and refund with correlation to the original PSP transaction ID
Webhook Security, Idempotency, and Ordering Guarantees
Given a webhook is received When the signature and timestamp are validated against the configured secret within a 5-minute tolerance Then invalid signatures are rejected with HTTP 401 and no state changes occur And valid events are acknowledged with HTTP 200 only after durable write to the event store And processing is idempotent using the PSP event ID as the deduplication key retained for at least 7 days And out-of-order delivery is resolved so that final order state reflects the latest applicable payment lifecycle (authorized, captured, failed, refunded) based on PSP timestamps
Resilient Retry, Backoff, and Dead-Lettering
Given any internal processing step (verification, persistence, bus publish, timeline write) fails transiently When the system retries Then exponential backoff with jitter is applied starting at 1s doubling to a max interval of 15m, retrying for at least 24h And a dead-letter queue captures events after retry exhaustion with error details and correlation ID And operational metrics are emitted with p95 end-to-end event-to-Paid latency under 2 seconds and p99 under 5 seconds during normal operations And success rate, failures, and DLQ depth are visible in the observability dashboard
Reconciliation View and One-Click Re-apply
Given a finance-role user opens the Reconciliation view for a date range or PSP transaction ID When platform state and PSP records differ Then mismatches are highlighted at the order and event level with reason categories (missing, extra, amount mismatch) And the user can trigger a re-pull by transaction ID that replays PSP events idempotently and updates the timeline/state And the action is recorded in an audit log with user, timestamp, and before/after state And the view returns results within 3 seconds for the last 7 days and supports CSV export
Tips & Amount Modifiers
"As a guest, I want clear tip options during one‑tap checkout so that I can reward good service without extra steps."
Description

Present configurable tip suggestions (e.g., 0%, 10%, 15%, 20%) and a custom amount within the wallet or saved‑card flow. Support merchant‑level defaults, per‑store overrides, tax/service fee rules, and dynamic total updates in the payment sheet. Persist tip amounts to reporting, exports, and staff payouts. Ensure clear, accessible UI and that selections are reflected in all supported payment methods without extra steps.

Acceptance Criteria
Select Suggested Tip With Dynamic Total Update
Given an order with a $24.00 item subtotal, 8% item tax, no service fee, configured tip suggestions [0%, 10%, 15%, 20%], and tip base = item subtotal When the guest opens the One‑Tap Wallet link and selects the 15% tip suggestion Then the UI displays a tip of $3.60 and updates the total to $24.00 + $1.92 tax + $3.60 tip = $29.52 within 500ms And the primary payment button(s) reflect the updated total before authorization
Enter Custom Tip With Validation and Rounding
Given an order with an $18.50 item subtotal and custom tip entry enabled with a default maximum of 50% of subtotal When the guest enters a custom tip value of 2.5 Then the system interprets it as $2.50 in the order currency, rounds to two decimals, and updates the total within 500ms And if the guest enters a negative, non‑numeric, or a value exceeding 50% of subtotal, an inline error is shown, the tip is not applied, and payment cannot be submitted
Per‑Store Tip Suggestion Overrides
Given merchant defaults for tip suggestions are [0%, 10%, 15%, 20%] and Store A overrides them to [0%, 12%, 18%, 22%] When a guest opens the One‑Tap Wallet for Store A Then the displayed tip suggestions are [0%, 12%, 18%, 22%] in that order And when a guest opens the One‑Tap Wallet for a store without overrides, the displayed suggestions are [0%, 10%, 15%, 20%]
Tax and Service Fee Rules Reflected in Totals
Given a $20.00 item subtotal, a $2.00 service fee (taxable), an 8.5% tax rate applied to items and service fee, and tips configured as non‑taxable When the guest selects a 15% tip Then the system calculates tip = $3.00 on the subtotal, tax = $1.87 on $22.00, and total = $26.87 And the payment payload itemizes subtotal, tax, service fee, and tip in minor units with currency code
Tip Reflected Across Apple Pay, Google Pay, and Saved Card Without Extra Steps
Given a selected tip of 18% is applied in the wallet When the guest chooses Apple Pay Then the Apple Pay sheet shows the total including the tip before biometric confirmation, with no additional tip step When the guest chooses Google Pay Then the Google Pay sheet shows the total including the tip before confirmation, with no additional tip step When the guest pays with a saved card Then the confirmation screen shows the total including the tip and charges the saved card accordingly
Persist Tip to Reporting, Exports, and Staff Payouts
Given an order completes with a $4.20 tip at Store A When settlement is recorded Then the order record includes tip_amount_cents = 420, tip_percent, currency, store_id, and payment_method fields And CSV exports include columns tip_amount, tip_percent, total_with_tip, payment_method, and store_id for the order And the Staff Payouts report attributes $4.20 in tips to Store A for the correct payout period And the Reporting API returns the tip fields for the order
Accessible Tip UI in SMS Web Wallet
Given the wallet is opened on a mobile device When navigating tip options and custom tip input with a screen reader and keyboard-only Then each tip button has an accessible name (e.g., “Tip 15 percent”), focus order is logical, and color contrast for text/buttons is ≥ 4.5:1 And custom tip errors are announced via an ARIA live region and associated to the input And after selecting a tip, focus remains on or returns to the payment control without additional steps
PCI & Compliance Guardrails
"As an operator, I want payments handled within compliant guardrails so that my business reduces risk and audit burden."
Description

Keep CurbPing in PCI DSS SAQ A scope by using PSP‑hosted wallets/fields and tokenization; never store, process, or transmit PAN. Enforce TLS 1.2+, HSTS, and secure cookies; rotate secrets and Apple/Google credentials; and maintain auditable logs of payment events and admin actions. Implement 3DS/SCA where required, consent capture for card saving, and privacy/legal notices in the SMS link flow. Add WAF/rate limiting, anomaly detection, and quarterly reconciliation checks to reduce risk and chargebacks.

Acceptance Criteria
SAQ A Scope via PSP-Hosted Wallets and Tokenization
Given a guest opens the SMS payment link When the checkout renders payment options Then all card data entry fields and wallet buttons are hosted by the PSP (or their JS components/iframes), with no PAN, CVV, or expiry handled by CurbPing code outside PSP components. Given payment is submitted via Apple Pay, Google Pay, or PSP-hosted fields When the PSP returns a payment_method token Then only a tokenized identifier and non-sensitive metadata (brand, last4, expiry month/year) are stored; no full PAN/CVV/unmasked expiry is stored, logged, or transmitted by CurbPing. Given application logs, request/response payloads, analytics, and error traces When inspected in staging and production Then they contain no PAN or sensitive authentication data; automated DLP scans run daily and block deployment on violations. Given annual PCI scope review When SAQ type is determined Then the service remains eligible for SAQ A with evidence (PSP attestation, data flow diagrams, DLP results) attached to the compliance record.
Transport Security and Cookie Hardening on SMS Payment Flow
Given any request to the SMS payment domain When TLS is negotiated Then TLS version is 1.2+ and weak ciphers (e.g., RC4, 3DES) are disabled; SSL Labs grade is A or better. Given successful HTTPS responses When headers are inspected Then HSTS is present with max-age >= 15552000, includeSubDomains, and preload. Given session or preference cookies are set When cookies are created Then all cookies are Secure, HttpOnly, and SameSite=Lax or Strict; no cookies are set on non-TLS origins. Given SMS payment links are generated When a guest opens the link Then links use single-use, short-lived tokens expiring in <= 30 minutes of inactivity; expired/used tokens return 410 and require a regenerated link.
3DS/SCA Enforcement for Regulated Transactions
Given a card issuer or region requires SCA per PSD2/network rules When a card or wallet payment is initiated Then 3DS 2.x is invoked via the PSP; frictionless or challenge flow occurs as directed by the issuer. Given a 3DS challenge is required When the guest completes or abandons the challenge Then authorization occurs only on successful completion; declines/timeouts show issuer reason codes and are logged with 3DS outcome. Given a transaction is eligible for an exemption (e.g., TRA, Low-Value) When the PSP approves the exemption Then the transaction proceeds without challenge and the exemption type is recorded for audit. Given non-EEA cards or out-of-scope wallets When payment is processed Then 3DS is not forced and rationale (out-of-scope) is logged.
User Consent and Disclosures in SMS Checkout Flow
Given a guest is offered to save their card When the payment form renders Then the “Save my card for next time” option is unchecked by default with clear language on purpose and retention. Given a guest opts in to save their card When payment is authorized Then consent is recorded with user identifier (phone), timestamp, IP, policy/version IDs, and token reference; only tokens are stored. Given a guest withdraws consent When they use the manage-preferences link from an SMS receipt Then saved payment tokens are detached within 24 hours and further charges using them are blocked. Given the SMS checkout page loads When legal notices are displayed Then links to Privacy Policy and Terms are visible prior to payment, accessible on a standard mobile viewport, and clicks are tracked.
Auditable Payment and Admin Action Logging
Given any payment lifecycle event (initiation, wallet open, authorization, capture, refund, reversal, chargeback, 3DS outcome) When the event occurs Then an immutable audit record is written with timestamp (UTC), actor (user/admin/system), IP/UA, order ID, PSP IDs, outcome, and before/after state. Given an admin changes security-sensitive settings (keys, WAF/rate limits, roles) When the change is submitted Then the action requires MFA, is versioned, and the audit log captures who, what, when, old/new values (masked), and a change reason. Given auditors query logs When using the console or API Then at least 3 months are online-searchable and 12 months are retained; records are tamper-evident (hash-chained) and exportable in CSV/JSON. Given logs may contain PII When records are stored or displayed Then PAN/CVV are never logged; other PII is minimized/masked and access is role-restricted with access events audited.
Threat Controls: WAF, Rate Limiting, and Anomaly Detection
Given requests target payment endpoints (/pay, /wallet, /tokenize, /refund) When malicious patterns (e.g., SQLi, XSS, traversal) are detected by WAF rules Then requests are blocked with 403 and an alert is sent to on-call within 5 minutes. Given high-velocity activity from an IP, device, or phone number When thresholds are exceeded Then rate limits apply: max 10 payment attempts per 10 minutes per phone and 60 requests per minute per IP; offenders receive 429 with retry-after. Given geolocation/BIN-country mismatch or repeated 3DS failures When anomaly detection flags risk Then the transaction is held for review or forced to challenge, and a risk event with features and decision is logged. Given attack simulations in staging When nightly security test suites run Then >95% malicious probes are blocked by WAF and zero PAN leakage occurs; regressions fail the CI pipeline.
Operational Governance: Secrets Rotation and Quarterly Reconciliation
Given PSP API keys and Apple/Google Pay merchant certificates When rotation policy is enforced Then keys/certs are rotated at least every 90 days via KMS-backed secrets; dual keys support zero-downtime rotation with automated rollback on failure. Given a rotation occurs When changes are deployed Then previous secrets are revoked within 24 hours and audit logs record initiator, timestamp, and scope. Given monthly payouts and order records When quarterly reconciliation runs Then authorized/captured/refunded totals match PSP reports within 0.5% variance; discrepancies are ticketed and resolved within 5 business days. Given chargebacks are received When monthly metrics are reviewed Then chargeback rate stays below 0.9%; root-cause analysis is completed for spikes with remediation actions tracked.

Smart Tip Nudges

Automatically suggests fair, context‑aware tip options based on wait time, order size, and time of day. Friendly copy boosts tip acceptance without pressure, improving team earnings while keeping the payment flow to a single tap.

Requirements

Context-Aware Tip Scoring Engine
"As a curbside guest, I want tip options that reflect my experience so that choosing a fair amount feels easy and appropriate."
Description

Compute real-time, fair tip suggestions using order subtotal, measured curbside wait time, and time-of-day signals. Produce a ranked set of 3 contextually appropriate options plus a "No tip" and "Custom" path. Enforce configurable floors/ceilings and smoothing to avoid extreme suggestions from outliers (e.g., unusually long waits). Respect privacy by using only order/session telemetry and no sensitive attributes. Execute in under 100 ms with deterministic fallbacks when any signal is missing (e.g., default percentages). Suppress suggestions for comped orders or when discounts exceed a threshold. Expose outputs via an internal API contract consumed by checkout surfaces.

Acceptance Criteria
Real-time tip computation with all signals present
Given an order with subtotal > 0, a measured curbside wait time, and a known time-of-day within operating hours When the scoring engine computes tip suggestions Then it returns exactly five options: three ranked suggestions, one explicit "No tip" (amount = 0), and one "Custom" selection path And the three suggestions are returned in ranked order from lowest to highest amount and are labeled as suggested options And the suggestions are computed using all three inputs (subtotal, wait time, time-of-day) such that holding subtotal and time-of-day constant, increasing wait time by 10 minutes does not decrease the middle suggestion amount And holding wait time and time-of-day constant, increasing subtotal does not decrease any suggestion amount And outputs include a deterministic rank order for identical inputs across repeated calls
Deterministic fallbacks when signals are missing
Given one or more signals are missing or invalid (e.g., wait time unavailable, time-of-day missing, or subtotal not provided) When the scoring engine computes tip suggestions Then it returns exactly five options using the configured default percentage set for the three suggestions, plus "No tip" and "Custom" And the output includes a flag indicating that fallback defaults were applied And for identical inputs with the same missing signals, repeated calls return identical suggestion amounts and ordering And if subtotal is missing, suggestion amounts are computed deterministically from configuration (e.g., applied to an assumed baseline amount) rather than arbitrary values
Outlier smoothing and floor/ceiling enforcement
Given inputs that include outlier conditions (e.g., very long wait times or unusually high subtotals) When the scoring engine computes tip suggestions Then each suggested percentage is bounded within the configured floor and ceiling values, excluding the "No tip" and "Custom" paths And increasing wait time beyond the configured smoothing threshold increases suggestions by no more than the configured maximum slope per minute And decreasing wait time below the smoothing threshold lowers suggestions no faster than the configured minimum slope per minute (non-oscillatory behavior) And no suggestion violates the configured floor/ceiling even under extreme inputs
Tip suggestion suppression for comped or heavily discounted orders
Given an order marked as fully comped OR an order with a discount percentage greater than or equal to the configured suppression threshold When the scoring engine evaluates the order Then it returns a response indicating tip suggestions are suppressed and does not include suggested tip options And for an order with a discount percentage below the threshold and not comped When the scoring engine evaluates the order Then it returns suggestions normally (five options as defined)
Internal API contract: payload structure and versioning
Given a valid API request containing session/order identifiers and available signals (subtotal, wait time, time-of-day) When the scoring engine responds Then the response is JSON and conforms to the internal schema including: options[5] with type for each (suggested/noTip/custom), amount and currency for suggestions, ranked=true for suggested options, suppressed boolean, signalsUsed[], configVersion, and timestamp And the API includes an explicit apiVersion field and rejects requests with unsupported versions with a 4xx error and helpful message And field names, types, and required/optional status match the published contract, validated by automated schema checks And the response includes no additional undeclared fields
Performance and reliability under load
Given a production-like environment at a sustained 100 requests per second with concurrent requests (>=50) When load testing runs for 10 minutes Then the p95 end-to-end execution time of the scoring engine is <= 100 ms and error rate is < 0.1% And cold-start requests (first request after deployment) complete in <= 200 ms p95 And no timeouts or queue overflows are observed in logs/metrics during the test window
Privacy and telemetry constraints
Given only order and session telemetry are provided to the engine When computing suggestions Then the engine does not read or require any sensitive attributes (e.g., demographic data) and does not use phone number or license plate data in scoring And the request and response schemas exclude sensitive attributes by design And logs and metrics emitted by the engine do not include PII and redact identifiers per policy And attempts to pass unsupported sensitive attributes are ignored and flagged with a validation error without affecting computation
One-Tap Tip UI
"As a mobile user completing payment curbside, I want to add a tip with one tap so that checkout is fast and stress-free."
Description

Present tip suggestions in a mobile-first, single-tap interface embedded in the web payment sheet and SMS deep-link flow. Use friendly, non-coercive copy and clear labeling that tipping is optional. Provide 3 dynamic options and a custom amount, with no preselected default. Ensure WCAG AA accessibility, large tappable targets, and full keyboard support. Guarantee sub-300 ms render time on 3G, idempotent submissions to prevent double charges, and seamless dismissal. Localize currency and formatting per locale.

Acceptance Criteria
Single-Tap Tip Selection Submits Payment
Given the user opens the CurbPing web payment sheet via browser or SMS deep link and no tip is selected When the user taps any one of the three suggested tip options Then the payment is submitted immediately without an extra confirmation step And the UI shows a processing state within 100 ms And on success, a payment confirmation is displayed And on failure, an inline error is shown with a Retry button and the previously selected tip preserved
Optional, No-Default Tip with Seamless Dismissal
Given the tip UI is visible Then no tip option is preselected And the text "Tipping is optional" is visible and programmatically associated with the tip group And a visible, focusable "No tip" or "Skip" control is present When the user selects "No tip"/"Skip" or dismisses the tip UI Then the flow continues without adding a tip and without page reload And the transition to the next step completes within 150 ms at p95 And no copy includes the words "required", "must", or "mandatory"
API-Driven Dynamic Tip Suggestions with Fallback
Given order total, estimated wait time, and local time of day are available When the tip UI loads Then it requests tip suggestions from the backend with those parameters and renders exactly 3 suggestion options plus a Custom option as returned And if the suggestions API fails or exceeds 200 ms, the UI renders default 10%, 15%, and 20% options plus Custom And no option is selected by default And the Custom option opens an input to enter a tip amount or percentage and is fully functional
WCAG AA and Keyboard Accessibility
Given the tip UI is rendered Then all interactive elements meet a minimum contrast ratio of 4.5:1 And each tappable target (options, Custom, Skip) has a hit area of at least 44x44 CSS pixels And the UI is fully operable with keyboard only: Tab/Shift+Tab move focus in logical order, and Space/Enter activate the focused control And a visible focus indicator is present with at least 3:1 contrast And all controls have accessible names announced by screen readers that include the tip amount and type (e.g., "15 percent tip") And Esc dismisses the tip UI where a dismiss action is available And no keyboard traps are present
3G Render Performance Under 300 ms
Given a mid-tier device under a Fast 3G network profile When the payment sheet or SMS deep-linked page is opened Then the tip options become visible and interactive within 300 ms (p95 over 20 runs) from navigationStart as measured by a "tip_options_rendered" performance mark
Idempotent Submission Prevents Double Charge
Given the user taps a tip option and submission is in progress When the user taps any tip option again within 500 ms or a network retry occurs Then only one payment is created and charged And the server responds with the same transaction id for duplicate idempotency keys And the UI displays a single confirmation and no duplicate charge occurs
Locale-Aware Currency and RTL Layout
Given the user's locale and currency are available from the session or browser When the tip UI renders Then currency symbols, decimal separators, and number grouping follow the user's locale (e.g., 1.234,56 € for de-DE; ¥1,200 for ja-JP) And RTL locales (e.g., ar, he, fa, ur) render the tip UI in right-to-left direction with mirrored layout and correctly ordered amounts And if locale is unknown, the UI falls back to en-US formatting without error
Merchant Tip Controls
"As a restaurant owner, I want to configure how tip suggestions behave so that they match my policies, branding, and team payout rules."
Description

Offer an owner/operator settings panel to enable/disable Smart Tip Nudges, choose default percentage bands or fixed amounts, set min/max caps, and schedule daypart-specific configurations. Allow brand-friendly copy presets, suppression rules (e.g., large catering orders), and pooling/payout preferences. Provide role-based access control, per-location overrides, and safe preview mode before publishing. Changes propagate within 5 minutes and are versioned for rollback.

Acceptance Criteria
Toggle Smart Tip Nudges at Merchant Level
Given a merchant admin views Tip Controls, When they toggle Smart Tip Nudges to On and publish, Then newly created orders started after publish display tip suggestions. Given Smart Tip Nudges are Off, When a customer checks out, Then no tip suggestions or copy are shown. Given the toggle state is changed, When the admin refreshes or logs in from another device, Then the saved state persists and is reflected consistently. Given an order was opened before the toggle change, When the customer completes payment, Then the tip experience remains as it was at order start.
Configure Tip Options and Schedules with Per‑Location Overrides
Given a merchant configures percentage bands (e.g., 15%, 18%, 22%), When a $40 order begins during an applicable schedule, Then the UI shows correctly computed amounts ($6.00, $7.20, $8.80) based on the subtotal before tax/fees as defined by configuration. Given a merchant configures fixed amounts (e.g., $1, $3, $5), When an order begins, Then those fixed options are displayed exactly as configured. Given min/max caps are set (e.g., min $0.50, max $10), When percentage-based tips are calculated, Then displayed amounts respect both caps (never below min, never above max). Given daypart schedules are defined (e.g., Breakfast 06:00–11:00, Dinner 17:00–21:00) in the location’s timezone, When an order starts within a daypart, Then the corresponding configuration is applied; otherwise the default configuration is applied. Given overlapping dayparts exist, When an order falls into both, Then the schedule with higher priority (as ordered in the UI) is deterministically applied. Given a location-specific override exists, When an order starts at that location, Then the override is applied; When an order starts at a location without an override, Then the global configuration is applied.
Brand‑Friendly Copy Presets with Safe Preview
Given an admin selects a copy preset and enables Preview Mode, When they open the preview session or test link, Then the tip prompt displays the selected copy and formatting only within the preview context. Given Preview Mode is active and changes are not published, When live customers check out, Then they do not see the new copy or configuration. Given the admin publishes the preset, When new orders begin, Then customers see the new copy on the tip prompt. Given tip nudges are enabled with this preset, When a customer proceeds through checkout, Then the number of taps to complete payment does not increase compared to the no‑nudge baseline flow.
Suppression Rules for Large/Catering Orders
Given a suppression rule “suppress for orders ≥ configured amount or tagged Catering” is enabled, When an order meets either condition, Then no tip nudges or tip copy are displayed. Given an order does not meet any suppression condition, When it proceeds to payment, Then the configured tip nudges and copy are displayed as per active settings. Given the suppression threshold is updated, When new orders begin after publish, Then the new threshold is used; orders already in progress retain their existing state.
Tip Pooling and Payout Preferences
Given pooling is enabled with equal‑share distribution, When $100 in tips are collected, Then the payout report allocates equal shares to the defined pool participants and the sum of allocations equals $100 within $0.01 rounding tolerance. Given pooling is disabled, When tips are collected, Then payouts attribute tips solely to the order’s assigned staff member. Given a per‑location pooling preference differs from global, When orders are fulfilled at that location, Then the payout calculations and exports use the location‑specific preference.
Role‑Based Access Control and Audit Trail
Given a user with Owner or Manager role signs in, When they navigate to Settings > Tips, Then the Merchant Tip Controls panel is visible and editable. Given a user with Staff or Cashier role signs in, When they navigate to Settings > Tips or attempt direct URL access, Then access is denied (HTTP 403 or equivalent) and no settings are revealed. Given any change is published, When viewing the audit log, Then an entry exists capturing user, timestamp, affected scope (global or specific location), and version identifier.
Change Propagation (≤5 min) and Versioned Rollback
Given a change is published at time T, When a new order begins after T, Then the new configuration is applied no later than T+5 minutes. Given a rollback to version V is initiated at time R, When a new order begins after R, Then version V is applied no later than R+5 minutes. Given version history exists, When an admin selects a prior version and confirms restore, Then the system creates a new current version mirroring the selected prior state and records it in the audit log.
Compliance and Transparency Safeguards
"As a customer, I want clear and honest tip information so that I understand what I’m paying and don’t feel pressured."
Description

Ensure legal and ethical presentation of gratuities: clearly label tips as optional and separate from service charges/taxes, display live-calculated amounts next to percentages, and show who benefits (e.g., staff pool) where required. Adapt terminology and defaults by region, honor tax/gratuity regulations, and log consent artifacts for audits. Provide receipt line items and refund/chargeback behavior consistent with processor rules. Include content guidelines to prevent pressure tactics.

Acceptance Criteria
Tip Optional and Separate From Charges at Checkout
- Given a customer at payment, When the tip UI renders, Then the component label includes the word "optional". - Given the cart has subtotal, taxes, and any service charge, When a tip is selected or deselected, Then taxes and any service charge amounts remain unchanged. - Given the payment summary, When the customer reviews totals, Then tip, service charge, and tax appear as distinct line items with amounts and the total equals subtotal + service charge + tax + tip within ±0.01 of the transaction currency. - Given the tip UI, When the customer chooses "No tip", Then it is applied in a single tap and no additional confirmations are required.
Live Percentages with Calculated Tip Amounts
- Given percentage options (e.g., 10%, 15%, 20%), When the order total changes due to item edits or discounts, Then each option displays the recalculated currency amount within 100 ms of the change. - Given the customer taps a percentage, When the amount is applied, Then the charged tip equals round(order_total * percent, currency minor unit) and the UI shows that exact amount. - Given a custom tip entry, When the customer inputs an amount, Then the UI displays the derived percent to one decimal and enforces currency rounding, rejecting invalid entries with inline error.
Beneficiary Disclosure Next to Tip
- Given a venue configuration with tip beneficiaries, When the tip UI renders in a region requiring disclosure, Then a note (e.g., "Tips go to staff tip pool") appears within one line below the options and is localized to the session language. - Given a venue that allocates tips only to front-of-house, When disclosure renders, Then the copy states the correct beneficiary group per configuration. - Given consent logging, When the customer submits payment, Then the exact disclosure text and locale are captured in the audit record.
Non‑Coercive Copy and Defaults
- Given regional compliance rules, When preselection is disallowed, Then no tip option is preselected; otherwise, a compliant default not exceeding the configured maximum is allowed and a visible "No tip" option is present. - Given content guidelines, When the tip UI renders, Then disallowed coercive terms (e.g., "must", "required", "penalty") are not present; a content linter blocks violations in CI and at runtime falls back to compliant copy. - Given the customer declines tipping, When "No tip" is tapped, Then no nag screens or additional prompts appear and the choice persists for the session.
Regional Terminology and Tax/Gratuity Rules
- Given the user's region and venue settings, When the tip UI renders, Then labels use region-appropriate terms (e.g., US: "Tip", UK: "Gratuity", QC: "Pourboire") and never mislabel a service charge as a tip. - Given VAT/GST rules by region, When the total is computed, Then VAT/GST is not applied to tips unless explicitly configured for that region; automated tests cover US, CA (including QC), UK, and EU at minimum. - Given jurisdictional rules for preselection and default percentages, When defaults are computed, Then the values conform to the regional compliance config file validated at startup.
Consent and Audit Logging
- Given payment submission, When the customer completes checkout, Then the system logs timestamp, venue ID, order ID, currency, region code, compliance config version, tip selection (amount and percent or zero), UI copy hash, and consent indicator with PII redaction. - Given storage policy, When logs are written, Then they are immutable, retained for at least 24 months, and retrievable by authorized roles via export within 5 seconds for a 30-day range. - Given a dispute, When an auditor requests evidence, Then the system can reconstruct the tip UI state (options, amounts, labels, disclosure text, locale) from the log without contacting the customer.
Receipts, Refunds, and Chargebacks Alignment with Processor
- Given the receipt (SMS link and printable), When it is generated, Then tip, service charge, tax, subtotal, and total appear as separate line items and match the processor metadata fields exactly. - Given a partial refund, When staff selects a refund amount, Then the system enforces processor-specific rules for tipping (e.g., proportional or full tip refund), applies them correctly, and records the behavior in the audit log. - Given a chargeback, When processor events are ingested, Then the tip component status mirrors the processor outcome and any discrepancies are flagged within 24 hours for manual review.
Experimentation and Optimization Framework
"As a product manager, I want to experiment with tip suggestions so that we can maximize acceptance without hurting conversion."
Description

Enable A/B and multivariate testing across copy, option counts/order, percentages vs. amounts, and trigger timing. Provide feature flags, cohort targeting, and progressive rollout with guardrails (e.g., do not reduce checkout conversion beyond a set threshold). Track key metrics: tip acceptance rate, average tip, overall conversion, and time-to-complete. Supply a self-serve console for variant setup and automated significance checks, with easy rollback.

Acceptance Criteria
A/B copy test with conversion guardrail auto-rollback
- Given an experiment with control "Copy A" and treatment "Copy B" and a checkout conversion guardrail of -2% absolute, When the rolling 30-minute treatment conversion rate is lower than control by ≥2% and the difference is statistically significant at α=0.05 with minimum 500 sessions per arm, Then the platform pauses treatment and routes 100% of new traffic to control within 2 minutes. - Given the auto-rollback triggers, When the pause occurs, Then an alert is sent to Slack and email with timestamp, sample sizes, effect size, and p-value, and an audit log entry is created.
Multivariate tip option layout test (percent vs amount x option count)
- Given a 2x3 multivariate test with factors Tip Type {percent, fixed-amount} and Option Count {2,3,4}, When activated for eligible cohorts, Then traffic is split evenly across 6 cells with assignment variance ≤1% after 5,000 sessions. - Given the test is running, When metrics are computed hourly, Then the dashboard reports tip acceptance rate, average tip per order, overall checkout conversion, and time-to-complete per cell with 95% CIs. - Given significance monitoring is enabled, When any cell beats baseline on tip acceptance without degrading checkout conversion by more than 0.5% absolute at α=0.05, Then the system flags the winning configuration.
Feature flags and progressive rollout for Smart Tip Nudges
- Given a feature flag "smart_tip_nudges", When toggled on for 5% of traffic, Then only targeted sessions receive the nudge and assignment is sticky for the checkout session. - Given a scheduled ramp plan {5%, 10%, 25%, 50%, 100%}, When guardrails remain satisfied in each 30-minute interval, Then the rollout auto-advances; otherwise it halts and rolls back to last safe percentage within 60 seconds. - Given a manual rollback is requested, When executed, Then no user mid-checkout sees the UI change before submitting, and new sessions see the flag off within 60 seconds.
Cohort targeting and mutual exclusion across experiments
- Given cohorts defined by store ID, time of day, order size buckets, and returning vs new customer, When an experiment is configured, Then inclusion/exclusion rules correctly filter traffic with ≥99% rule-match accuracy in sampled logs. - Given two experiments target overlapping users, When mutual exclusion is enabled, Then a user is assigned to at most one experiment within a 7-day window. - Given experiment caps set to max 2 concurrent experiments per store, When a third is scheduled, Then it queues with a visible status and start ETA.
Self-serve experiment console for setup and monitoring
- Given a user with Experimenter role, When creating a new experiment, Then they can define variants, select primary KPI and guardrails, choose cohorts, set rollout plan, and save without backend intervention. - Given an active experiment, When viewing the console, Then the user sees live metrics, sample sizes, estimated power, MDE, and significance status updated at least every 5 minutes. - Given an experiment is ended, When the user clicks Export, Then a CSV with assignment, metrics, and decision summary is downloadable within 30 seconds.
Automated significance and stopping rules
- Given the primary KPI is tip acceptance rate, When calculations run, Then a two-proportion z-test with sequential correction (e.g., Pocock boundary) is applied and the current α-spent is displayed. - Given the secondary KPI is average tip amount, When calculations run, Then Welch’s t-test is applied with normality not assumed, and 95% CIs are shown. - Given stopping rules are configured with max duration 21 days and max sample 50,000 sessions per arm, When either criterion is met or significance is achieved with guardrails satisfied, Then the system recommends stop and can auto-stop if enabled.
Event tracking, data quality, and metric integrity
- Given events TipViewed, TipSelected, CheckoutCompleted with session and variant IDs, When a session occurs via the SMS 'I'm here' link, Then events are emitted exactly-once with end-to-end latency ≤3 seconds at the 95th percentile. - Given daily reconciliation runs, When comparing experiment metrics to the payments ledger, Then differences in average tip and tip acceptance are ≤1% relative. - Given an outage occurs, When backfill processes run, Then no more than 0.5% of sessions are missing from experiment datasets and gaps are annotated in the console.
Tip Analytics and POS/Payroll Reconciliation
"As a manager, I want accurate tip reporting and payouts so that my team is compensated correctly and finance can reconcile deposits."
Description

Aggregate and expose tip KPIs by location, daypart, staff/bay, and campaign. Provide exports and webhooks to sync per-order tip amounts to POS and payroll systems, honoring pooling rules. Handle adjustments, refunds, and chargebacks with traceable audit trails and idempotent retries. Flag anomalies (e.g., sudden drops) with alerts. Ensure data retention and privacy policies align with merchant agreements.

Acceptance Criteria
Tip KPIs Aggregation and Filtering
- Given a dataset of ≥100k orders across multiple locations and time zones, When a user selects a date range, location(s), daypart(s), staff, bay, or campaign filters, Then the dashboard displays total tips, tip rate (% of orders with tip), average tip per order, median tip %, and P90 tip % accurately and recalculates within 2 seconds for ≤100k orders. - Given location-specific time zones, When a date range is applied, Then aggregations use each location’s local time and totals match a control SQL query within ±0.1%. - Given currency and percentage display rules, When KPIs render, Then currency shows two decimals with correct symbol and percentages show one decimal.
Sync to POS/Payroll via Webhooks and Scheduled Exports
- Given a new or updated tip event (create, adjust, refund, chargeback), When a webhook subscription is active, Then a POST is sent within 5 seconds containing event_id, event_type, occurred_at, order_id, location_id, gross_tip_cents, tip_percent, currency, pooling_rule_id, allocations[], and adjustment_type; the request is HMAC-SHA256 signed and uses TLS 1.2+. - Given a non-2xx webhook response or 5s timeout, When retries are attempted, Then exponential backoff with jitter is used for up to 24 hours and the same event_id is reused to ensure idempotency. - Given a user schedules an export with filters and a destination (S3 or SFTP), When the schedule triggers, Then a CSV is delivered within the scheduled window with deterministic filename, SHA-256 checksum, and columns: order_id, order_ts, location_id, staff_id(s), staff_role(s), bay_id, campaign_id, gross_tip_cents, tip_percent, pooling_rule_id, allocation_json, adjustment_type, adjustment_ref, net_tip_cents, currency, timezone. - Given privacy constraints, When exports are generated, Then customer PII (e.g., phone) is excluded and any payment token is masked to last 4.
Pooling Rules Enforcement and Allocation Accuracy
- Given an active pooling rule (e.g., 80% FOH servers, 20% BOH kitchen weighted by hours), When orders receive tips during a shift, Then allocations per staff are computed proportional to hours on-duty and role, rounded to cents with remainder distributed fairly and the sum of allocations equals gross_tip less adjustments. - Given overlapping shifts or multi-bay assignments, When allocations are computed, Then priority rules are applied as configured and results are deterministic and repeatable. - Given a pooling rule change effective at timestamp T, When orders occur after T, Then new rules are applied; historical orders remain unaffected unless an authorized re-allocation is executed and logged. - Given recorded unpaid breaks, When hours are calculated, Then break intervals are excluded from allocation weighting.
Adjustments, Refunds, and Chargebacks with Audit Trail
- Given a refund or chargeback on a tipped order, When the event is processed, Then a negative tip adjustment is created that preserves the original record, updates exports, and emits a webhook reflecting the delta. - Given a manager-initiated tip correction, When submitted with a required reason code, Then allocations are recalculated and an immutable audit entry captures actor, timestamp, IP, reason, before/after values, and related order_id. - Given cumulative negative adjustments on an order, When totals would fall below zero, Then the net tip is floored at zero and the order is flagged for review.
Anomaly Detection and Alerting
- Given a 14-day rolling baseline by location and daypart, When today’s tip rate or average tip per order deviates by >30% or >3σ, Then an alert is sent within 10 minutes to configured email and optional SMS recipients with context (location, daypart, metric, baseline, current). - Given an alert is marked as false positive, When similar conditions recur within 7 days, Then alerts are suppressed unless deviation exceeds 60%. - Given multiple locations, When an anomaly affects several, Then the dashboard highlights impacted entities with sparklines and a link to a detail view.
End-to-End POS/Payroll Reconciliation Accuracy
- Given a completed business day per location in local time, When comparing per-staff net tip totals produced by CurbPing against POS and payroll system totals for the same period, Then the absolute difference is ≤ $1 or ≤ 0.5%, whichever is greater, and any variance report lists the top 10 variance drivers. - Given late adjustments posted after day close, When D+1 reconciliation runs, Then deltas are included and tagged with source and timestamp, and updated totals are re-sent via webhook/export with a clear revision sequence. - Given daylight saving time changes, When day boundaries shift, Then reconciliation aligns to local business rules (e.g., 4 AM close) and documents the boundary used.
Data Retention, Privacy, and Access Controls
- Given a merchant retention policy of 24 months, When records exceed policy age, Then per-order tip events are deleted or irreversibly anonymized weekly while aggregate KPIs are retained. - Given role-based access controls, When a user without Finance role accesses Analytics, Then per-order allocation details are hidden and staff names are masked to initials; exports are inaccessible. - Given data subject access/deletion requests, When executed by an authorized admin, Then all related tip events are exported/deleted within 30 days and an audit entry is recorded. - Given encryption requirements, When files are exported to S3/SFTP, Then server-side encryption (AES-256 for S3) is enabled and only strong ciphers are permitted for SFTP connections.

Instant Add-Ons

Surface ready‑fast extras (drinks, desserts, sauces) within the payment link, filtered by real‑time kitchen capacity. Guests add with one tap; items route to expo immediately so average order value rises without delaying pickup.

Requirements

Inline Upsell in Payment Link
"As a curbside guest, I want to quickly add a drink or sauce from the payment page so that I can enhance my order without extra steps or delays."
Description

Embed a dynamic add‑ons module directly within the CurbPing payment link page that displays ready‑fast extras (e.g., drinks, desserts, sauces) with images, prices, and one‑tap add behavior. The module loads from a merchant‑managed catalog, respects capacity/availability rules, and updates the order total instantly without page reload. It functions seamlessly in mobile browsers launched from SMS, maintains fast page performance (<2s LCP on 4G), supports quantity adjustments, and degrades gracefully if capacity data is unavailable. The upsell section remains non‑blocking (guests can pay without engaging) and is compatible with existing CurbPing order identifiers and curbside workflows.

Acceptance Criteria
Display Ready-Fast Add-Ons from Catalog
Given a valid payment link tied to an order and a merchant-managed catalog containing ready-fast items When the payment link page loads in the browser Then the upsell module is visible within the payment page and lists only ready-fast add-ons pulled from the catalog And each listed add-on shows an image, name, price, and a primary "Add" control And catalog data reflects the latest published state at render time (fetched at load, no older than 60 seconds)
One-Tap Add and Quantity Adjustments with Instant Total Update
Given an add-on is displayed in the upsell module When the guest taps "Add" Then the quantity for that item becomes 1, the order subtotal/total update within 200ms without a full page reload, and the order summary reflects the change Given an item has quantity ≥ 1 When the guest taps "+" or "-" Then the quantity increments/decrements by 1 with the total recalculated within 200ms; when quantity reaches 0 the item is removed from the order summary and the control returns to "Add" Given a network error occurs during an update When the system receives a non-2xx response Then an inline error message appears, the UI reverts to the last confirmed state within 2 seconds, and payment remains available
Real-Time Capacity Filtering
Given capacity/availability data is available from the kitchen capacity service When the upsell module fetches availability Then only items with capacity = true are displayed and items with capacity = false are hidden Given a displayed item’s availability changes to false after render When the module refreshes availability (at least every 30 seconds) Then that item is hidden within 30 seconds; if already added to the order, it remains but cannot be increased (increase control disabled) Given an item’s availability changes to true When the next refresh occurs Then the item is eligible to be displayed and added
Graceful Degradation When Capacity Data Is Unavailable
Given the capacity service is unavailable (timeout > 1s or 5xx) When rendering the upsell module Then the module still renders using catalog defaults, allows adding items, and payment flow is not blocked And the UI indicates availability status is unknown without showing an error state And no JavaScript uncaught exceptions occur; errors do not prevent checkout completion
Mobile SMS-First Compatibility
Given the guest opens the payment link from an SMS on iOS Safari or Android Chrome (latest two major versions) When the page loads on a mobile viewport (≥ 320px width) Then the upsell module renders without horizontal scrolling, interactive controls have a minimum touch target of 44x44px, and CLS attributable to the module is ≤ 0.1 And no app install prompts or redirects occur; all actions complete in-browser
Performance Targets on 4G
Given a simulated 4G connection (≈150ms RTT, 1.6 Mbps down, 750 Kbps up) on a mid-range device When loading the payment link cache-cold Then LCP ≤ 2.0s and the upsell module becomes interactive ≤ 3.0s Given image assets for add-ons When first rendered Then images are lazy-loaded, served as WebP/AVIF, and each is ≤ 60KB transferred Given a cache-warm reload When reloading the page Then LCP ≤ 1.2s and the upsell module is interactive ≤ 1.0s
Order Integration and Non-Blocking Checkout
Given an existing CurbPing order identifier is present in the payment link When add-ons are added and payment is completed Then the added items are attached to the same order ID, appear on the receipt/order summary, and are dispatched to expo within 2 seconds of payment confirmation Given the guest does not interact with the upsell module When tapping Pay Then checkout proceeds without any additional required steps and no validation errors related to the upsell are shown Given curbside workflows execute after payment (bay assignment, arrival detection) When the order is processed Then these workflows remain unaffected and use the same order identifier
Real‑Time Capacity Filter
"As an operator, I want the system to show only add‑ons the kitchen can make right now so that guests don’t choose items that slow down pickup."
Description

Determine add‑on eligibility using live kitchen capacity signals so that only items that can be prepared without delaying pickup are shown. Inputs include operator‑defined per‑item prep times, current queue load, throttle rules, time to guest ETA, daypart schedules, and 86’d items. The client re‑evaluates eligibility in real time (e.g., via SSE/WebSocket or short polling) and hides or disables items that exceed the allowed incremental prep threshold. Provide clear visual states (available, low stock, unavailable) and ensure state changes are atomic to prevent race conditions during selection.

Acceptance Criteria
Eligibility Calculation with ETA and Prep Threshold
Given an add-on with prep time Pt and current queue delay Qd And the guest ETA is E minutes from now And the allowed incremental prep buffer is B minutes And the item is in active daypart, not 86’d, and throttle not exceeded When Pt + Qd <= E - B Then the item is displayed as Available and selectable When Pt + Qd > E - B Then the item is not eligible and is excluded from selection
Real-Time Re-evaluation via SSE/WebSocket and Polling Fallback
Given an active SSE/WebSocket connection When any capacity signal changes (queue load, throttle counters, 86’d status, daypart, ETA) Then affected items’ eligibility and visual state update within 1 second without page reload Given the real-time connection drops When the client switches to short polling Then the client polls at most every 5 seconds and reflects server changes within 6 seconds of the source change
Visual States: Available, Low Stock, Unavailable
Given an item is eligible and stock S > low-stock threshold L Then the state shows Available and the Add button is enabled Given an item is eligible and 0 < S <= L Then the state shows a Low stock badge and the Add button remains enabled Given an item is ineligible due to capacity, S == 0, 86’d, or outside daypart Then the state shows Unavailable and the Add button is disabled
Atomic Selection During State Changes
Given an item transitions from Available to Unavailable while a user taps Add When the add request is processed Then either the add is committed exactly once and capacity counters update, or the add is rejected with a "No longer available" notice and the cart remains unchanged And no duplicate adds or charges occur And the client reflects the definitive outcome within 1.5 seconds of tap
Throttle Rules Enforcement
Given a throttle rule of max K units per rolling M minutes for item X And K units have been added in the last M minutes When a user attempts to add item X again within that window Then the attempt is blocked, the item shows Unavailable, and a throttling message is displayed When the rolling window drops below K Then eligibility is recomputed and the item state updates within 1 second
Daypart and 86’d Filtering
Given an item is outside its configured daypart or is marked 86’d When the payment link is opened or a real-time update arrives Then the item is hidden from the add-ons list And if the item is in the cart and unpaid, it is removed and the user is notified within 1 second
One‑Tap Add and Immediate Expo Routing
"As an expo lead, I want add‑on items to hit the make screen immediately with a rush indicator so that the line can start them without holding the original order."
Description

On guest tap, add the item to the order, confirm quantity, update totals, and dispatch a rush‑tagged line item to the expo/make screen or kitchen printer with the order ID, bay (if assigned), and clear add‑on flag. Ensure idempotency (ignore rapid duplicate taps), handle offline/retry scenarios with queued events, and guarantee delivery to expo via at‑least‑once messaging with deduplication. Route items only after successful incremental authorization to avoid waste, with an operator setting available for “prep on auth” vs. “prep on capture.” Provide guest confirmation and staff alerts without interrupting existing order flow.

Acceptance Criteria
One‑Tap Add Updates Order and Totals
Given a guest has an open payment link for an active curbside order And at least one add-on item is available When the guest taps "Add" on an item Then a new line item is created with quantity=1, correct name/modifiers, and associated orderId And the order subtotal, tax, and total update within 500 ms on the client and 1 s on the server And the UI shows the updated total and an inline quantity control When the guest adjusts quantity within allowed limits Then totals recalculate in real time and the pending additional charge equals unit price*qty + tax And the updated order state is persisted and retrievable via API within 1 s
Idempotent Tap Handling
Given an add-on item is displayed When the guest taps the "Add" control multiple times within 2 seconds or the same request is retried due to network issues Then only one line item is created and only one incremental authorization attempt is made And duplicate client requests within a 60-second dedup window return a 200 OK idempotent response without side effects And no more than one dispatch message is sent for the same clientRequestId+itemId+orderId
Offline Add‑On Queue and Retry
Given the client is offline or a transient network error occurs When the guest taps "Add" Then the request is queued locally with a clientRequestId and the UI shows a non-blocking "Pending..." state And retries occur with exponential backoff for up to 6 attempts or 5 minutes, whichever comes first When connectivity is restored within the retry window Then the request processes exactly once via idempotency and the UI/totals update within 1 s When retries are exhausted or a permanent 4xx error is received Then no routing occurs and the guest sees a failure message with a "Try Again" option
Incremental Authorization Gate for Routing
Given the original order has a payment method that supports incremental authorization When the guest confirms the add-on quantity Then the system performs an incremental authorization for the additional amount (subtotal + tax) And if authorization is approved, the order total is updated and the add-on is eligible for routing And if authorization is declined or partially approved, no routing occurs, a decline message with code is shown, and the guest can retry or remove the item And no capture is attempted at this step when the operator setting is "prep on capture"
Operator Setting: Prep on Auth vs Prep on Capture
Given an operator can configure the add-on routing policy as "prep on auth" or "prep on capture" And the setting change is saved in admin Then the setting takes effect for new add-on attempts within 60 seconds When "prep on auth" is active and incremental auth is approved Then the add-on is routed immediately after auth approval When "prep on capture" is active Then the add-on is routed only after capture succeeds And if capture fails after auth, no routing occurs and the auth is voided or reversed
Guaranteed Expo/Kitchen Dispatch with Deduplication
Given an add-on is eligible for routing When dispatching to expo/make screen and/or kitchen printer Then a rush-tagged message is published containing orderId, lineItemId, itemName, qty, modifiers, addOnFlag=true, and bayNumber if assigned And delivery uses at-least-once semantics with retries until acknowledgment or up to 5 attempts And the expo/printer deduplicates by lineItemId to render/print exactly once And the expo shows the add-on within 2 seconds of acknowledgment with a visible "ADD-ON RUSH" flag and bay number if available And printed tickets include "ADD-ON RUSH", orderId, bay number (if assigned), item, qty, and timestamp
Guest Confirmation and Non‑Interrupting Staff Alerts
Given an add-on has been routed per the configured setting Then the guest sees a confirmation within 1 second showing item, quantity, updated total, and pickup bay if assigned And, when SMS is enabled, a confirmation SMS is sent within 5 seconds And the expo UI surfaces a non-blocking alert on the existing order card without modal dialogs And other in-progress orders remain interactable with no frame drops exceeding 100 ms during alert render And the alert includes orderId, addOnFlag, quantity, item, and bay if assigned
Incremental Payment and Tax Handling
"As a guest, I want added items charged correctly and reflected on my receipt so that I know exactly what I paid for."
Description

Support incremental authorization/capture to seamlessly charge add‑ons within the existing payment session, calculating itemized taxes, fees, and tip allocation correctly. For already‑captured orders, support linked secondary charges with unified receipts. Handle declines by prompting the guest to retry, offering pay‑at‑curb fallback, or removing the item. Update digital receipts and send SMS confirmations on success, and ensure PCI‑compliant processing with idempotent keys and provider support (e.g., Stripe incremental auth). Synchronize totals with order records for accurate reporting and reconciliation.

Acceptance Criteria
Incremental Authorization for Add-Ons in Open Payment Session
Given an order has an active, uncaptured authorization and the guest adds add-ons within the same session When the guest confirms the add-ons via the payment link Then the system requests an incremental authorization for the additional amount on the existing payment intent And an idempotency key scoped to orderId+addonSet+attempt is used And the provider returns an approved incremental authorization with the updated authorized total And the final capture includes both the original and add-on amounts in a single capture event And the add-on items are time-stamped and marked ready-to-expo immediately upon authorization success
Accurate Itemized Tax, Fees, and Tip Allocation for Add-Ons
Given jurisdictional tax rules and item taxability are configured When a guest adds one or more add-ons Then tax is computed per line item using the correct tax rate and standard rounding to 2 decimals And any service fees are recalculated based on configured fee rules and added as separate lines And tip allocation is applied per configuration: tip remains unchanged by default; if auto-apply-to-addons is enabled, the original tip is proportionally allocated across base items and add-ons without increasing the total tip And the payment summary shows itemized prices, taxes, fees, and tip allocation for each line item and totals match the sum of components
Linked Secondary Charge for Already-Captured Orders
Given the original order payment has already been captured and the guest adds add-ons When the guest confirms payment for the add-ons Then a new payment intent is created for the incremental amount and linked to the original via metadata/transfer_group/orderId And only the incremental amount is charged in the secondary capture And a unified digital receipt is generated showing both the original charge and the linked secondary charge as one order with distinct line items And the order total equals the sum of original and secondary charges; no duplicate charging of original items occurs
Decline Handling with Retry, Pay-at-Curb Fallback, or Item Removal
Given an incremental authorization or secondary charge attempt is declined or times out When the guest is notified of the failure Then the UI offers Retry (up to 2 additional attempts), Pay at curb, and Remove item options And selecting Retry re-attempts the charge with a new attempt idempotency key and surfaces provider decline codes to the logs And selecting Pay at curb marks the add-ons as unpaid, flags the order for curbside collection, and notifies staff in the console And selecting Remove item deletes the add-ons from the order and prevents expo routing for those items And no success SMS is sent on failure; only on successful charge or confirmed removal is the receipt updated
Digital Receipt Update and SMS Confirmation on Success
Given an incremental authorization or linked secondary charge succeeds When the payment provider confirms success Then the digital receipt updates within 5 seconds to include new line items with itemized taxes, fees, and tip allocations And the receipt displays provider charge identifiers for both original and incremental transactions And the guest receives an SMS confirmation containing the updated total and a link to the unified receipt And the receipt history shows a chronological record of original and incremental payment events
Idempotency and Provider Support Compliance
Given network retries or duplicate taps occur for the same add-on set within a short window When multiple identical payment requests are sent Then exactly one financial transaction is created; subsequent requests return the original result using the same idempotency key And audit logs capture the idempotency key, provider request IDs, and outcomes for each attempt And if the provider does not support incremental auth for the current payment method, the system falls back to a linked secondary charge with a clear reason code And all provider interactions use PCI-compliant SDKs/APIs and do not expose sensitive PAN data
Order Record Synchronization and Reconciliation
Given any add-on payment attempt results in success, failure, or cancellation When the order state is updated Then order records reflect accurate subtotals, taxes, fees, tips, captured amount, and outstanding balance And reporting attributes revenue and taxes to base items vs add-ons using distinct categories And daily reconciliation totals match provider settlement for the day with $0.00 variance for successful charges And partial outcomes (e.g., some items removed) are reflected so that order totals equal the final charged amount
SLA Guardrails and Guest Messaging
"As a time‑pressed guest, I want to know if an add‑on will delay my pickup so that I can decide quickly."
Description

Estimate the add‑on’s impact on pickup time using prep‑time models and current queue state. If the predicted delay exceeds an operator‑defined threshold (e.g., +2 minutes), block the item or require explicit guest confirmation while displaying a clear ETA change. Show inline badges such as “Ready fast” or “May delay pickup,” and never auto‑extend ETAs without guest consent. Persist the final ETA in the order record and notify staff of any approved changes so curbside handoff timing remains accurate.

Acceptance Criteria
Predictive ETA Impact Computation
Given an order with a base ETA (T0), live queue state, and station prep-time models When the guest opens the payment link and add-ons are fetched Then the system computes predicted ETA impact (delta_seconds) per add-on using the current queue and prep-time models And the computation accounts for item make time, station concurrency, and WIP And delta_seconds is an integer >= 0 and matches model expectations within ±30 seconds in test fixtures And the top 20 add-ons have computed delta_seconds available to the UI within 200 ms
Operator SLA Threshold Policy Enforcement
Given an operator-defined delay threshold in seconds (threshold_s) and a policy in {Block, Confirm} And a candidate add-on with predicted delta_seconds > threshold_s When the guest attempts to add the item Then if policy = Block, the Add action is disabled and a tooltip/message states "Blocked: exceeds SLA" And if policy = Confirm, a modal shows the new ETA (T0 + delta_seconds) and requires an explicit "Accept ETA" action And if the guest cancels or closes the modal, the item is not added and the ETA remains T0
No Auto-Extension of ETA Without Consent
Given an add-on selection that yields delta_seconds > 0 When the guest has not performed an explicit "Accept ETA" action for the current cart state Then the displayed ETA remains T0 and the backend order ETA remains T0 And payment submission is blocked until consent is captured or the delaying items are removed And only after "Accept ETA" does the displayed and backend ETA update to T0 + cumulative_delta
Inline Badges and ETA Disclosure
Given computed delta_seconds for each add-on When rendering the add-on list in the payment link Then items with delta_seconds = 0 display the exact badge text "Ready fast" And items with delta_seconds > 0 display the exact badge text "May delay pickup" And when an add-on with delta_seconds > 0 is tapped, the UI shows the precise ETA change formatted as +Xm Ys prior to confirmation
Final ETA Persistence and Staff Notification
Given the guest accepts a new ETA and completes payment When the order is created/updated in the system Then the order record stores final_eta = T_final and eta_source = "guest_approved" and eta_delta_seconds = cumulative_delta And a staff notification is emitted within 2 seconds including order_id and T_final and reason = "ETA updated by guest" And the curbside/staff-facing screen reflects T_final within 5 seconds of payment confirmation
Multiple Add-Ons Cumulative Impact and Consent
Given the guest selects multiple add-ons When delta_seconds are recalculated per item considering concurrency and overlap Then the UI displays a single consolidated ETA delta (cumulative_delta) for the cart And if cumulative_delta > threshold_s under Confirm policy, a single consent covers the consolidated ETA And if a subsequent selection increases cumulative_delta, the prior consent is invalidated and the guest must re-accept before payment
Real-Time Recalculation on Capacity Changes Before Payment
Given kitchen capacity or queue state changes while the guest is on the payment link When the recalculated cumulative_delta differs from the last shown value by ≥ 30 seconds Then badges and the ETA delta update in the UI within 1 second And any prior consent becomes invalid and the guest is prompted again to accept the updated ETA And if the new cumulative_delta exceeds threshold_s under Block policy, previously selectable items become blocked and cannot be added
Merchant Add‑Ons Catalog and Rules
"As an operator, I want to configure which extras appear and when so that upsells align with my kitchen’s capacity and menu."
Description

Provide an operator dashboard to curate add‑ons: create/edit items, images, prices, tax codes, categories, modifiers (e.g., sauce flavor), max quantities, and dayparts. Configure per‑item prep times, capacity profiles, throttle limits, and maximum allowed incremental prep minutes. Enable quick 86/un‑86, per‑location overrides, CSV import/POS sync, and preview of how items will render in the payment link. Changes should propagate in near‑real time to live sessions with audit logging for compliance and rollback capability.

Acceptance Criteria
Create/Edit Add-On with Modifiers, Dayparts, and Quantity Limits
Given an operator with edit permissions for Location A is in the Add-Ons dashboard When they create an item with name, price, tax code, category, image, a modifier group "Sauce Flavor" (required), max quantity = 3, and dayparts set to 11:00–15:00 and 17:00–21:00 (location timezone) Then the item saves successfully and appears in the dashboard preview with the configured fields and required modifier selection And the max quantity control caps selection at 3 in the preview And the item is not shown in the preview when the simulated time is outside its dayparts, and is shown when within And an audit log entry is recorded with actor, timestamp, and before/after values When they edit the item (change price and add a new modifier option) and save Then the preview reflects the changes within 5 seconds And a new audit log entry records the change delta
Enforce Per-Item Prep Time and Max Incremental Prep Minutes in Offers
Given item X has basePrepTime = 4 minutes and maxIncrementalPrepMinutes = 3 And a guest has an active payment link session with current ETA T When availability is computed for the session Then item X is hidden or disabled if adding it would increase the guest’s ETA by more than 3 minutes And item X remains offerable if the computed incremental impact is <= 3 minutes, with the updated ETA surfaced to the session When an operator updates maxIncrementalPrepMinutes for item X to 5 Then the change takes effect in live sessions within 5 seconds
Capacity Profile and Throttle Limits Filter Real-Time Availability
Given capacity profile "Drinks" with a throttle limit of 10 adds per 5-minute window And items A and B belong to "Drinks" When the running count in the current window reaches 10 Then items A and B are hidden or marked unavailable in guest sessions within 5 seconds And when the window rolls over or capacity drops below the limit Then items A and B become offerable again within 5 seconds And capacity consumption is debited on add and credited on cancel/rollback
Instant 86/Un-86 Propagates to Live Sessions with Audit Trail
Given item Y is currently available When an operator toggles 86 on item Y in the dashboard Then item Y is removed from the dashboard preview and from all live payment link sessions within 5 seconds without requiring a page reload And an audit log entry records the actor, timestamp, and reason When the operator un-86s item Y Then item Y reappears in the preview and live sessions within 5 seconds And a corresponding audit log entry is recorded
Per-Location Overrides with Inheritance and Revert
Given a brand default for item Z has price $3.00 and dayparts 10:00–22:00 And Location B creates an override setting price $3.50 and dayparts 12:00–20:00 When availability and pricing are computed for Location B Then the override values are used in the preview and guest sessions And when the override is removed Then Location B reverts to the brand default within 5 seconds And the audit log shows both the creation and removal of the override
CSV Import and POS Sync Apply Valid Changes with Audit
Given an operator imports a CSV mapping columns to fields (name, price, tax code, category, modifiers, dayparts, prep time, capacity profile, max qty, max incremental minutes) When validation runs Then rows missing required fields (name, price) are rejected with a per-row error report, and valid rows are accepted And accepted rows create or update items accordingly And all accepted changes appear in the preview and live sessions within 10 seconds And an audit log entry captures the import file metadata and row counts (accepted/rejected) Given a POS sync payload with external item identifiers matches existing items When the sync completes successfully Then items are updated or created, conflicts are resolved per the configured source-of-truth, and changes propagate to live sessions within 10 seconds And sync results (successes/failures) are recorded in the audit log
Audit Log Detail and One-Click Rollback of an Item Version
Given item Z has historical versions v1 and v2 due to prior edits When an operator opens the audit log detail for item Z and selects "Rollback to v1" Then the system restores all fields for item Z to the values in v1 and records a new audit entry referencing the rollback source version And the restored state propagates to the dashboard preview and live sessions within 5 seconds And the pre-rollback state is preserved as a new version to allow roll-forward if needed
Conversion Analytics and Experimentation
"As a manager, I want visibility into which add‑ons convert and how they affect AOV and wait times so that I can optimize the upsell strategy."
Description

Capture impression, click, add‑to‑order, and conversion events to compute attach rate, incremental revenue, and impact on ETA/waits. Provide dashboards with segment filters (location, daypart, category) and export endpoints. Support A/B testing of placement, copy, default quantities, and pricing with statistically sound bucketing and holdouts. Respect privacy by excluding PII from analytics payloads and honoring opt‑out settings. Surface operator insights and recommendations (e.g., promote top converters, retire low performers).

Acceptance Criteria
Funnel Event Capture for Instant Add‑Ons
Given a guest opens the payment link with Instant Add‑Ons visible, When the page renders, Then an impression event is sent with non‑PII fields: event_id, order_id, session_id, location_id, addon_ids_shown, experiment_assignments, timestamp_ms. Given a guest taps an add‑on card, When the tap is registered, Then a click event is sent with addon_id, position, dedupe_key, timestamp_ms. Given a guest adds an add‑on, When the add action succeeds, Then an add_to_order event is sent with addon_id, quantity, unit_price, currency, timestamp_ms. Given the guest completes payment, When the order is confirmed, Then a conversion event is sent with addon_revenue, addon_items, payment_method_type (non‑PII), timestamp_ms and attributed only if within 30 minutes of impression. Then 99% of events are delivered end‑to‑end within 3 seconds and daily loss rate is ≤0.1%, with idempotent server‑side de‑duplication on dedupe_key.
Metric Computation Accuracy for Attach Rate and Incremental Revenue
Given a selected date range and location(s), When attach rate is computed as orders with ≥1 add‑on ÷ eligible orders, Then the value matches order ledger ground truth within ±0.5 percentage points at daily granularity. Given the same scope, When incremental revenue from add‑ons is computed, Then the total matches the sum of add‑on line items in the order system within ±1.0%. Given experiment holdout data, When impact on ETA and wait time is computed, Then median and p90 deltas match operations logs within ±10 seconds. Then metrics refresh at least hourly and display last refresh timestamp.
Dashboard Segmentation and Performance
Given an operator opens the analytics dashboard, When no filters are set, Then the default timeframe is last 7 days in local timezone and granularity is daily. Given the operator applies filters for location(s), daypart(s), category(ies), date range, and experiment, When Apply is clicked, Then all charts and tables refresh within 2 seconds p95 and reflect the filters. Given a sharable link is copied, When opened by another user, Then the same filters are pre‑applied from the URL state. Then no PII is rendered in any dashboard view.
Analytics Export API Endpoints
Given valid OAuth credentials, When requesting GET /analytics/instant‑addons/events with a date range ≤31 days, Then the API returns JSON or CSV with schema_version, paginated via cursor, excluding PII fields. Given a large export (≥100k rows), When requested, Then the API responds with p95 latency ≤5 seconds per page and supports continuation until complete without duplication or omission. Given rate limits are exceeded, When additional requests arrive, Then the API returns 429 with Retry‑After. Then the aggregates endpoint returns attach rate, incremental revenue, and guardrail metrics aligned to dashboard filters and matches dashboard values within ±0.5%.
A/B Testing Bucketing, Holdouts, and Reporting
Given an experiment with variants (placement, copy, default quantity, pricing) and a 10% holdout is activated, When eligible traffic arrives, Then users are randomly and deterministically bucketed by order_id with allocation within ±1% of targets and are sticky for 30 days. Given multiple experiments target the same surface, When activation is attempted, Then the system enforces one active experiment per surface or isolates via namespaces to prevent interaction. Given assignments occur, When analytics are recorded, Then an assignment event is logged alongside funnel events for attribution. Given live traffic, When allocation deviates by >3 standard deviations (SRM), Then an alert is raised and the experiment is flagged. Given experiment analysis is run, When results are computed, Then lift, 95% confidence intervals, and guardrail deltas (ETA/waits) are displayed and downloadable.
Privacy and Opt‑Out Compliance
Given any analytics payload, When inspected, Then it contains no PII (no names, phone numbers, emails, license plates, or free‑text fields) and passes automated PII lint rules prior to transmission. Given a guest has Do Not Track enabled or opts out via the payment link privacy control, When they interact, Then no analytics events are sent and they are excluded from aggregates and experiments. Given a location‑level analytics opt‑out, When enabled by the operator, Then all analytics and experiments are disabled for that location within 15 minutes. Then analytics data are retained for 13 months and deleted thereafter per policy.
Operator Insights and Recommendations
Given ≥14 days of data, When the insights job runs nightly, Then it surfaces top 10 converting add‑ons and categories and flags the bottom decile by conversion and revenue contribution. Given recommendations are generated, When viewed, Then each includes rationale, expected impact range, and one‑click actions to promote, adjust price/quantity, or retire items. Given an operator accepts a recommendation, When applied, Then configuration updates propagate within 10 minutes and the change is tracked with timestamp and internal user identifier. Then the dashboard reports realized impact versus forecast after 7 and 14 days.

Split Pay

Let guests split balances across multiple cards or corporate/personal in two taps. CurbPing SMS sends companion links for each payer and issues separate receipts—ideal for families, office orders, and gig drivers needing reimbursement.

Requirements

Multi-Payer Link Orchestration
"As a guest initiating a curbside order, I want to send SMS pay links to multiple people so that each of us can quickly pay our share without installing an app."
Description

Generate unique, secure payment links for multiple payers from a single curbside order, delivered via SMS from CurbPing. The initiator can choose split mode (even, custom amounts, or by items) and enter recipients. Each link expires, is single-use, and deep-links to a lightweight, mobile-friendly checkout that works without apps or hardware. The system tracks invite delivery, retries failed SMS, supports link resends, and prevents duplicate charges by locking links upon successful authorization. Integrates with existing order timeline so staff can see when invites are sent and who has engaged, without exposing payer card data.

Acceptance Criteria
Even Split via SMS for Three Payers
Given a curbside order with grand total T and the initiator selects Split Pay: Even for N=3 and enters three valid phone numbers When the initiator confirms the split Then the system generates three unique, single-use HTTPS payment links with cryptographically strong tokens (>=128-bit entropy) And sends one SMS per payer containing their link And logs "Split Pay created" and three "Invite sent" events to the order timeline with masked phone numbers And each link pre-fills the payer amount = round(T/3, 2) with the final link adjusted so the sum equals T And each link deep-links to a mobile-friendly checkout that requires no app or hardware And upon successful authorization of a payer amount, the corresponding link is locked and cannot be reused And the order is marked "Payment complete" only when the sum of successful authorizations equals T And a separate receipt is issued to each payer for their amount.
Custom Amount Split With Remaining Balance Management
Given an order total T and the initiator selects Split Pay: Custom and enters payer amounts A1..An and phone numbers When the initiator confirms Then the system blocks confirmation unless sum(Ai) = T and each Ai >= the configured minimum charge amount And generated links reflect the assigned amounts per payer And partial completion updates the remaining balance in real time on the order timeline And the initiator can add an additional payer link to cover any remaining balance before fulfillment And each successful authorization locks its link and prevents duplicate charges And separate receipts are issued per payer with their exact amounts.
Split by Items With Accurate Tax and Discount Allocation
Given an order with line items, taxes, fees, and discounts and the initiator selects Split Pay: By Items When the initiator assigns items to specific payers and confirms Then the system computes each payer total including proportional tax, fees, and discounts with rounding rules that ensure the sum of payer totals equals the order total And prevents assigning the same unit of an item to more than one payer And each payer’s checkout displays only their assigned items and total And successful authorizations lock the corresponding links and record item-level attribution on receipts and the timeline And if the order’s items change before all payments complete, outstanding links are invalidated, payers are shown "Link invalidated," and the initiator is prompted to reissue updated links.
Secure Link Expiration and Single-Use Enforcement
Given payment links have a configurable time-to-live (TTL) When a payer opens a link within TTL Then the checkout loads over HTTPS and allows exactly one successful authorization for the specified amount And after a successful authorization or upon TTL expiry, the link transitions to a non-payable state and displays an appropriate message ("Link already paid" or "Link expired") And any subsequent attempts to reuse the token are rejected without creating additional charges And resending after expiry generates a new token and revokes the old token.
SMS Delivery, Retry, and Resend Observability
Given invites are sent via SMS When the system dispatches messages Then it records provider message IDs and delivery status updates (queued, sent, delivered, failed) per recipient And on carrier failure or no-delivery within the configured window, it retries up to two times with backoff and logs each attempt And the initiator can resend an individual invite from the order screen; active links are reused, expired or revoked links are replaced with new links And the timeline shows each send, delivery, open, and payment event with timestamps And no SMS content includes card data; only the secure link and order context are included.
Staff Timeline Visibility Without Card Data Exposure
Given staff need visibility into split payment progress When viewing the order timeline Then staff see per-payer entries showing masked phone number, split mode, assigned amount, link status (Sent/Delivered/Open/Authorized/Failed/Expired), and timestamps And no cardholder data is displayed (no PAN, no last-4, no card brand, no authorization codes) And staff cannot access payer checkout pages without the unique link And administrative exports include only payer labels and amounts, never raw card data.
Split Allocation Engine
"As an order initiator, I want to choose how the bill is split so that the amounts each person pays are fair and accurate."
Description

Provide flexible allocation of the order total across payers, supporting equal splits, custom fixed amounts, and item-level assignment with automatic distribution of taxes, fees, and tips according to local rules. Enforce validation so allocated amounts reconcile exactly to the order total, apply rounding rules that are fair and predictable, and enforce minimum per-payer thresholds. Allow the initiator to edit splits until any payment is captured; after partial payments, prevent changes that would invalidate settled amounts while enabling rebalancing of remaining balances if a payer drops out.

Acceptance Criteria
Equal Split with Proportional Taxes/Fees/Tips
Given an order with itemized subtotal, taxes, fees, and tip configured And the initiator selects an equal split across N payers When the system computes allocations Then each payer’s share is based on subtotal divided by N before taxes/fees/tip And taxes, fees, and tip are distributed proportionally to each payer’s subtotal share per local rules And rounding is applied to the smallest currency unit with residual cents balanced by largest-remainder method; ties resolved by payer creation order And each payer’s allocated total meets or exceeds the configured minimum per-payer threshold; otherwise the system blocks confirmation with a clear validation error And the sum of all allocated components equals the exact order total
Custom Fixed Amount Allocation with Validation
Given the initiator enters fixed amounts for each payer When values are edited Then the UI shows real-time remaining/unallocated amount and validation errors And each payer’s amount must be >= the configured minimum per-payer threshold And the sum of payer amounts must equal the exact order total after rounding And the Confirm button remains disabled until validation passes And on submit, allocations are saved atomically and an audit record is created
Item-Level Assignment with Localized Tax Logic
Given items are assigned to specific payers And taxes/fees/tip may include item-level and order-level components When allocations are computed Then item-level taxes and discounts follow the item’s assignee And order-level fees and tip are split proportionally to each payer’s assigned item subtotal unless overridden by configured local rule mappings And non-taxable items remain non-taxed regardless of assignee; taxable items apply the correct rate per jurisdiction configuration And each payer’s total equals sum(item prices + item-level taxes/discounts + proportional order-level fees + proportional tip) after rounding And totals across payers reconcile exactly to the order total
Deterministic Rounding and Fairness
Given allocations produce fractional cents When rounding is performed Then all components are rounded to the smallest currency unit using round-half-up And residual cents are assigned using largest-remainder method until reconciliation And tie-breakers are resolved deterministically in this order: higher pre-round share, then earliest payer added, then payer ID ascending And no payer deviates from their pre-round share by more than one smallest currency unit And rounding adjustments are captured in the audit log per payer
Editability Before Capture; Constraints After Partial Payments
Given no payments have been captured When the initiator edits split method, amounts, or item assignments Then the system allows edits and recalculates allocations with full validation Given one or more payers have captured payments When the initiator attempts edits that would reduce a settled payer’s allocation below their captured amount or reassign paid items Then the system blocks the change with a clear error and preserves settled allocations And only unpaid portions may be rebalanced among unpaid payers subject to thresholds and rounding rules And the UI marks settled portions as locked
Payer Dropout and Rebalancing
Given a payer is removed by the initiator before paying When the removal is confirmed Then the removed payer’s link is invalidated immediately And the unpaid balance is redistributed according to the current allocation method (equal or proportional) among remaining payers And redistribution respects minimum per-payer thresholds and rounding rules; if thresholds cannot be met, the system prompts to reduce payer count or adjust method And if a replacement payer is added, a new payment link is generated and allocations update accordingly
Over/Underpayment and Concurrency Safeguards
Given multiple payers may attempt payment concurrently via SMS links When a payer opens the payment page Then the page displays their current payable amount sourced from the server And payment capture is rejected server-side if it would cause collected total to exceed the order total or exceed the payer’s current balance, with a refreshed amount shown And atomic transactions ensure only one of concurrent captures succeeds; others receive a recoverable error with retry guidance And partial authorizations adjust the payer’s remaining balance and rebalance unpaid amounts as permitted without violating settled allocations
Secure Checkout with Tokenization and 3DS
"As a payer, I want a secure mobile checkout that works in two taps so that I can confidently pay my share from the SMS link."
Description

Implement a PCI SAQ-A compliant hosted checkout that tokenizes card data client-side with the payment processor, never touching CurbPing servers. Support mobile-friendly SCA/3-D Secure challenges where required, per payer and per link, and enforce rate limiting and anti-fraud checks to mitigate card testing. Use signed, ephemeral link tokens bound to order, payer, and amount; verify webhooks for payment events; and isolate payer views to prevent access to others’ info. Store only minimal metadata necessary for reconciliation and receipts.

Acceptance Criteria
Hosted Checkout Tokenization (SAQ-A)
Given a payer opens the Split Pay checkout link on mobile When the checkout loads Then all card entry fields are hosted by the processor (different domain/iFrame or redirect) and no PAN/CVV fields are served from CurbPing domains Given a payer submits payment When observing CurbPing network requests and server logs Then no PAN, CVV, or full expiry data are transmitted to or stored on CurbPing servers; only a processor token and minimal, non-sensitive metadata are stored Given a payment method is created When confirming the charge Then CurbPing uses only the processor-provided token and never handles raw card data
Per-Payer SCA/3DS Challenge Handling
Given a payer uses a 3DS/SCA-enforced card When confirming payment through the hosted checkout Then a mobile-friendly 3DS challenge is presented and, upon successful authentication, the payment is confirmed without re-entering card details Given a payer cancels or fails a 3DS challenge When retrying within the same link session Then the payer may retry up to 3 times before the session requires a new link Given a card/region not subject to SCA When confirming payment Then no 3DS challenge is shown and the payment proceeds frictionlessly Given multiple payers on the same order When each payer confirms payment Then 3DS flows are evaluated per payer and independently of other payers
Ephemeral Signed Payment Links
Given a payment link is created for a payer When inspecting the token Then it is cryptographically signed and binds order_id, payer_id, amount, and an expiry of 60 minutes Given the token is altered (order/payer/amount) or expired or already used When the link is opened Then access is denied with 401/403, no sensitive data is rendered, and the attempt is logged Given a valid, unused token When loading checkout Then the page displays only the bound amount and payer label and prevents editing of amount or payer
Rate Limiting and Anti-Fraud (Card Testing Mitigation)
Given repeated failed payment attempts on the same payer link When failures exceed 6 within 15 minutes Then further attempts on that link are blocked for 30 minutes and a generic error is shown to the user Given repeated failed attempts from the same IP/device across links When failures exceed 10 within 60 minutes Then additional attempts are blocked or require additional verification before proceeding Given a high-risk BIN or fraud signal returned by the processor When a payment is attempted Then the transaction is declined and the link remains usable unless rate-limit thresholds are exceeded Given a legitimate payer within thresholds When retrying after a single failure Then payment is allowed to proceed
Webhook Verification and Idempotent Reconciliation
Given a payment event webhook is received When validating the processor’s signature and timestamp Then only signed, timely events are accepted; invalid or replayed events are rejected with 4xx Given duplicate webhooks for the same event When processing Then state updates are idempotent and do not create duplicate charges or receipts Given a payment.succeeded event is validated When reconciling Then the payer and order balances are updated, and only minimal metadata (processor charge ID, amount, brand, last4, 3DS outcome) is stored—no PAN/CVV/full expiry Given a payment.failed or payment.canceled event is validated When reconciling Then balances remain unchanged and staff dashboards reflect the failure status
Payer Isolation and Access Control
Given two payers (A and B) on the same order When A opens A’s link Then only A’s bound amount and details are visible; B’s information is neither rendered nor retrievable via URL or API parameters Given A modifies identifiers in the URL or request to target B When requesting checkout or APIs Then the server responds 403 and returns no data about B Given an unauthenticated user without a valid token When requesting any payer or order endpoint Then no order or payer data is returned
Separate Receipts Per Payer
Given a payer completes payment successfully When the processor confirms the event Then CurbPing sends a receipt to that payer via SMS (and email if provided) within 30 seconds including amount, masked card (brand + last4), transaction ID, and 3DS status Given multiple payers on the same order When each completes payment Then each payer receives only their own receipt, and the merchant view shows an itemized breakdown per payer Given a partial or full refund is issued for a payer When a refund webhook is validated Then an adjusted receipt/credit note is sent to the affected payer only
Real-Time Payment Status Sync for Staff
"As a curbside staff member, I want to see which portions of an order are paid in real time so that I can time food release and pickup smoothly."
Description

Stream payment events to the staff dashboard and order timeline so team members can see per-payer statuses (invited, opened, authorized, captured, failed) and remaining balance at a glance. Trigger alerts to the kitchen when the order reaches full payment or predefined thresholds, and handle timeouts by reminding unpaid invitees. Reconcile race conditions and offline scenarios by consuming idempotent processor webhooks and retrying updates, ensuring the on-premise view stays consistent with processor state.

Acceptance Criteria
Dashboard Displays Per-Payer Real-Time Statuses
Given an order with split pay enabled and at least two invited payers And the staff dashboard is open on the order details view When the processor emits payment events for invitees (invited, opened, authorized, captured, failed) Then each payer’s status badge updates to the latest processor state within 2 seconds of webhook receipt And the order timeline appends a single entry per state change with timestamp and payer identifier And no duplicate timeline entries are shown for duplicated webhooks
Remaining Balance Updates in Real Time
Given an order total of $T and one or more payers And one or more captured payments totaling $C When a new payment is captured or a capture fails or is voided Then the remaining balance displayed equals $T − sum(captured payments) within 2 seconds And if remaining balance is $0.00, the order is marked Paid in the dashboard And pending authorizations are shown as “Pending” and do not reduce remaining balance
Kitchen Alert on Full Payment
Given kitchen alerts are enabled for full payment And an order has outstanding balance > $0.00 When captured payments reduce the remaining balance to $0.00 Then a kitchen alert is triggered within 2 seconds (visual banner and audible chime) And the alert is sent exactly once per order regardless of duplicate webhooks And a timeline entry “Full payment received” is recorded
Threshold Alert for Partial Payment
Given a payment threshold of 75% is configured for kitchen alerts And an order has remaining balance > 25% of total When captured payments reach or exceed 75% of the order total Then a kitchen “Threshold reached” alert is triggered within 2 seconds And the alert is sent at most once per order for the configured threshold And a timeline entry records the threshold and cumulative captured amount
Reminder SMS on Payment Timeout
Given payer A was invited via SMS with a unique payment link And the timeout is configured to 5 minutes without authorization or capture When 5 minutes elapse with payer A still unpaid Then a reminder SMS is sent to payer A with the same link And no more than 2 reminders are sent per payer, spaced at least 10 minutes apart And reminders stop immediately if payer A authorizes or captures payment And the timeline records each reminder with timestamp and recipient
Idempotent and Ordered Webhook Consumption
Given the system receives processor webhooks with idempotency keys and sequence numbers When duplicate or out-of-order events are received for the same payment intent Then only the latest valid state transition is applied to the payer and order And duplicates are ignored based on idempotency key without creating extra timeline entries And state regressions (e.g., captured -> authorized) are not applied And the last processed event id is persisted for audit
Offline Resilience and Retry Synchronization
Given the staff dashboard client loses network connectivity during an active order And the server continues receiving processor webhooks When the client reconnects Then the client requests and receives all missed events since its last acknowledged checkpoint And the dashboard reconciles to the processor-derived state within 5 seconds of reconnect And any failed client update deliveries are retried with exponential backoff up to 5 minutes And a “Re-synced with processor” timeline entry is logged once after reconciliation
Separate Receipts and Reimbursement Labels
"As a payer using both personal and corporate cards, I want separate, labeled receipts so that I can submit reimbursement without manual edits."
Description

Issue individual receipts per payer showing their paid amount, allocated items, tax, tip, and payment method, while also generating an order-level summary for the restaurant. Allow payers to label payments as personal or corporate, add memo fields (e.g., job number), and receive receipts via SMS link and optional email. Provide a lightweight export (CSV/PDF) for reimbursement and accounting, and enable receipt retrieval from the order history without exposing sensitive card data.

Acceptance Criteria
Per-Payer Itemized Receipt Generation
Given an order with items allocated across multiple payers via Split Pay When each payer’s payment is captured Then the system generates one separate receipt per payer And each receipt displays: order ID, receipt ID, local timestamp, payer label (Personal/Corporate), memo (if provided), allocated items (name, quantity, unit price, line subtotal), tax portion, tip portion, masked payment method (brand + last 4), and total paid And each payer receipt total equals the sum of its line subtotals + tax portion + tip portion within $0.01 And the union of allocated items across all payer receipts equals the full order’s items with no duplicates or omissions And the sum of all payer receipt totals equals the order grand total within $0.01
Payment Label and Memo Capture
Given a payer opens their Split Pay link When the payer selects Personal or Corporate (default Personal if none selected) and optionally enters a memo up to 100 characters Then the label and memo are validated (printable characters only), trimmed, and saved upon payment submission And the label and memo appear on the payer receipt, staff order summary, and in exports And the label and memo are editable until payment is submitted, and read-only afterward And if the memo exceeds 100 characters or contains disallowed characters, the payer sees a validation message and cannot submit until corrected
SMS Link and Optional Email Receipt Delivery
Given a payer successfully completes payment When capture is confirmed Then an SMS containing a secure receipt link is sent to the payer’s phone within 30 seconds (p95) And if a valid email address was provided, an email containing the same receipt link is sent within 60 seconds (p95) And the receipt link is a signed tokenized URL that expires after 90 days and contains no sensitive card data And opening the link renders the receipt in under 2 seconds for 95% of requests And failed SMS or email deliveries are logged with reason and can be retried by authorized staff from the order details view
Restaurant Order-Level Summary Receipt
Given an order uses Split Pay When any payer completes payment or the order is fully paid Then a restaurant-facing order summary is available in the dashboard within 5 seconds of the event And the summary lists for each payer: amount paid, items count, tax, tip, label, memo, masked payment method (brand + last 4), and receipt link And the summary displays outstanding balance (if any) and overall totals that equal the sum of payer totals within $0.01 And staff can print the summary from the dashboard and the layout fits A4 and Letter without truncation
Reimbursement and Accounting Export (CSV/PDF)
Given a staff user views an order or a date range of orders When they request an export Then a CSV is generated (UTF-8) with columns: order_id, order_timestamp_local, location, payer_ref(masked phone or link id), payer_label, payer_memo, items("name x qty @ unit_price"; semicolon-separated), subtotal, tax, tip, total_paid, payment_brand, payment_last4, receipt_url And a PDF export is generated with the order header and per-payer sections showing the same fields And both exports exclude PAN, expiry, CVV, and gateway tokens And exports use the restaurant’s local timezone for timestamps And exports up to 500 orders complete in under 10 seconds (p95)
Receipt Retrieval from Order History (Protected)
Given authorized staff open Order History in the dashboard When they view an order that used Split Pay Then they can view payer receipt summaries and resend receipt links via SMS or email to the original payer only And card details displayed are limited to brand and last 4; no PAN, expiry, CVV, or full transaction IDs are shown And accessing receipt data requires authenticated users with Orders:Read permission And all receipt views and resend actions are audit-logged with user, order ID, payer reference, and timestamp
Totals, Allocation, and Rounding Validation
Given an order total with tax and tip is split across multiple payers by allocated items When allocations are saved and payments are captured Then tax and tip are allocated proportionally to each payer’s item subtotals and rounded to the nearest cent using bankers’ rounding And per-payer totals never exceed the order grand total and no negative balances occur And any rounding remainder is assigned to the last payer so the sum of payer totals equals the order grand total And each item is assigned to exactly one payer; partial item splits are not permitted And submission is blocked if allocations do not cover all items or if the sum of payer totals does not equal the order total
Security and Privacy for Receipts
Given receipts are generated and shared via links When a link is created or accessed Then the receipt URL contains a short-lived signed token and no PII beyond masked payer reference And links can be revoked from the dashboard, immediately preventing further access And rate limiting is applied to receipt link access attempts (e.g., max 10 per minute per IP) And receipts display only masked payment details (brand + last 4) and omit PAN, expiry, CVV, and full gateway identifiers
Split Refunds, Voids, and Reconciliation
"As a manager, I want to refund the right portion to the right payer so that post-sale adjustments are accurate and auditable."
Description

Support partial and full refunds across split payments, ensuring refunds are issued against the correct payer and never exceed that payer’s captured amount. Provide staff tooling to select payers and amounts, propagate adjustments to receipts and reports, and handle full-order voids before capture. Reconcile processor settlements, fees, and chargebacks per payer, maintaining an auditable ledger of all split-payment events for compliance and customer support.

Acceptance Criteria
Partial Refund to Single Payer Within Captured Limit
Given an order with split payments and captured amounts recorded per payer And payer A has a refundable balance Y (captured minus prior refunds) When a staff user selects payer A and submits a partial refund amount X Then the system validates that 0 < X <= Y to the cent And creates a refund transaction linked to payer A’s original capture with a processor refund ID And updates payer A’s refundable balance to Y - X and the order’s totals accordingly And appends an immutable ledger entry with actor, timestamp (UTC), amount, currency, reason, and processor IDs And returns a success response within 5 seconds
Multi-Payer Partial Refunds Without Over-Refund
Given an order with multiple payers each with a refundable balance When staff issues refunds to multiple payers in sequence or batch Then each payer’s cumulative refunds must not exceed that payer’s captured amount And the order-level cumulative refunds must not exceed the total captured amount And concurrent refund attempts on the same payer are serialized to prevent race conditions And duplicate submissions are handled idempotently using an idempotency key to avoid duplicate refunds And UI balances and ledger reflect successful refunds within 2 seconds of completion
Full-Order Void Before Capture Across Split Payments
Given an order with authorizations across multiple payers and no captures recorded When staff initiates a full-order void Then the system sends void requests for each payer authorization and receives confirmation from the processor And marks the order and each payer’s transaction as Voided (not Refunded) And no settlement entries are created; only authorization and void events exist in the ledger And receipts show Voided status and $0 captured per payer And if any capture exists, the void action is blocked and staff are prompted to use the refund flow instead
Separate Receipts and Notifications Reflecting Refunds
Given payer A receives a partial or full refund When the refund succeeds Then payer A is sent an SMS with a secure receipt link within 30 seconds And payer A’s receipt displays original charge, refund amount(s), date/time, remaining balance (if any), and updated totals with correct tax/tip allocation And other payers’ receipts remain unchanged except for order-level notes that do not expose other payers’ personal data And staff can regenerate and resend any payer’s updated receipt on demand
Auditable Ledger of Split-Payment Events
Given any split-payment order When charges, captures, voids, refunds, disputes, or fee adjustments occur Then an append-only ledger entry is created per event including payer ID, event type, amount, currency, before/after balances, actor, timestamp (UTC), reason code, processor reference IDs, and idempotency key And ledger entries are immutable; corrections are represented by compensating entries And the ledger can be exported and filtered by order ID, payer, date range, and event type And totals reconcile to the order and per-payer balances within 0.01 of currency units
Processor Reconciliation Per Payer (Settlements, Fees, Chargebacks)
Given daily settlement, fee, and dispute reports from the payment processor When the reconciliation job runs Then settlements and fees are matched to per-payer captures/refunds via processor reference IDs And chargebacks are recorded against the correct payer with dispute reason, amount, and fee, adjusting per-payer balances And unmatched items older than 24 hours are flagged for review with a discrepancy alert And the reconciliation report shows per-payer gross, fees, net, refunds, disputes, and net deposit, matching processor totals within 0.01
Staff Refund Tooling UX and Safeguards
Given a staff user with refund permission opens the Split Refunds tool for an order Then the UI displays each payer’s label, last4, captured amount, prior refunds, and current refundable balance And the refund input supports $ amount and % of payer’s capture with correct rounding; values exceeding refundable balance are blocked with inline errors And selecting multiple payers requires explicit per-payer amounts; the tool does not auto-shift overages And submission requires a reason code and optional note; destructive actions present a confirmation modal And processor failures show clear errors and do not write ledger entries; partial successes are summarized and the user can retry failed payers only And the UI meets WCAG AA for contrast and keyboard navigation

Handoff Pre-Auth

Pre‑authorize the order at arrival and auto‑capture on handoff. If items change, totals update before capture so guests only pay for what they take. Fewer refunds and disputes, smoother curb flow for Captains and runners.

Requirements

Arrival Pre-Authorization Trigger
"As a curbside captain, I want the guest’s payment to be securely pre-authorized at arrival so that I can start the handoff knowing funds are available for the final amount."
Description

Initiate a secure payment pre-authorization when a guest arrives via the CurbPing "I’m here" link or detected arrival event. Calculate a pre-auth amount using the current order total plus a configurable overage buffer to cover substitutions or add-ons. Support card-on-file tokenization and an SMS-delivered, PCI-compliant payment link with Apple Pay/Google Pay for guests without a stored method. Do not store raw card data; tokenize through the gateway. Persist pre-auth ID, amount, and expiration, and associate it with the curb order, bay, and vehicle profile. Expose pre-auth state to staff in real time and block handoff if no valid pre-auth exists (configurable).

Acceptance Criteria
Pre-Auth on "I'm Here" Arrival With Card on File
Given a curb order with a stored payment token and a current total And an overage buffer is configured When the guest triggers arrival via the "I'm here" link Then the system sends a pre-authorization request to the payment gateway using the stored token without transmitting or storing raw card data And the pre-auth amount equals current order total plus the configured overage buffer, rounded to the smallest currency unit And the attempt is idempotent within a 5-minute window for the same order and arrival event And on gateway response, the pre-auth state is set to Authorized or Failed within 3 seconds And on success, the system persists pre-auth ID, amount, expiration timestamp, and associations to the curb order, bay, and vehicle profile
Pre-Auth on Auto-Detected Arrival Event
Given a curb order without a valid pre-authorization And an overage buffer is configured When the platform auto-detects guest arrival (without link interaction) Then exactly one pre-authorization request is initiated per order within a 5-minute deduplication window And the pre-auth amount equals current order total plus the configured overage buffer And duplicate arrival events during the window do not create additional pre-auths and instead return the existing pre-auth state And on success, pre-auth ID, amount, expiration, and associations to order, bay, and vehicle profile are persisted
SMS Payment Link for Guests Without Stored Method
Given a curb order with no stored payment token When an arrival event is received Then an SMS with a PCI-compliant hosted payment link supporting Apple Pay and Google Pay is sent to the guest's phone on file within 5 seconds And the link is hosted by the payment gateway; CurbPing does not handle raw card data And upon successful wallet authorization or card entry, a payment token is created via the gateway and stored tokenized And the system immediately attempts pre-authorization with the new token and updates pre-auth state within 3 seconds of tokenization And if the link is not completed within 15 minutes, it expires and the staff UI indicates "No valid pre-auth"
Real-Time Staff UI Pre-Auth State
Given a staff user is viewing the curb order in the Captain console When a pre-authorization attempt is initiated or completed Then the UI displays the pre-auth state in real time (Pending, Authorized, Failed, Expired) within 1 second of backend update And displays the pre-auth amount and expiration timestamp And provides a control to resend the payment link when no token is on file And audit events are recorded with timestamp and actor for each state change
Handoff Block Without Valid Pre-Auth (Configurable)
Given the location setting "Require pre-auth before handoff" is enabled And the order does not have an Authorized, unexpired pre-authorization When staff attempts to mark the order as handed off Then the action is blocked with a clear error stating a valid pre-auth is required And the Complete/Handoff control remains disabled until a valid pre-auth exists And when the setting is disabled, the same action is allowed regardless of pre-auth state
Pre-Auth Expiration and Refresh
Given an Authorized pre-authorization exists When the pre-authorization expires before handoff Then the system updates the state to Expired and notifies staff in the UI within 1 second And staff can trigger "Refresh Pre-Auth" to re-initiate pre-authorization using the latest order total plus buffer And the new pre-auth replaces the expired one and is persisted with new ID and expiration And expired pre-auths are never captured
Data Integrity, Security, and Audit
Given any pre-authorization workflow executes Then raw card data is never stored in application databases, logs, or analytics; only gateway tokens and permitted card metadata (brand, last4) are persisted And all pre-auth records persist pre-auth ID, amount, currency, expiration, gateway, order ID, bay ID (if assigned), and vehicle profile ID (if available) And sensitive fields are encrypted at rest and access is restricted to authorized services only And gateway failures are logged with redaction of PAN/CVV/full name And transient gateway errors are retried with exponential backoff up to 3 attempts and the final state is surfaced in the UI
Handoff Auto-Capture Engine
"As a runner, I want payment to auto-capture when I complete the handoff so that guests pay only for what they take and I can move to the next car quickly."
Description

Automatically capture payment on the staff "Handoff" event (e.g., runner confirms delivery, bay scan, or one-tap complete) using the active pre-auth. Determine the final capture amount from accepted items only, applying promotions, taxes, and tips, and support partial handoffs across multiple bags with cumulative capture up to the pre-auth ceiling. Generate and send a digital receipt via SMS, update order state to Paid, and log success/failure with retry-safe idempotency. If capture fails, immediately alert staff with guided remediation (retry, alternate method) without blocking lane flow.

Acceptance Criteria
Single-bag handoff auto-captures pre-auth with promos, tax, and tip
Given an order with an active pre-authorization and all items marked accepted And configured promotions, taxes, and a recorded tip amount When a staff member triggers the Handoff event Then the system calculates the capture amount from accepted items only, applies promotions, taxes, and tip per configuration, and captures that amount against the pre-authorization And the capture response is recorded with processor capture ID and timestamp And the order payment_state is set to Paid and operational state reflects Handoff Complete And an SMS receipt is sent to the guest within 10 seconds of capture success And a success audit log entry is created including order ID, idempotency key, pre-auth ID, capture ID, amount, and actor
Partial handoff supports multi-bag cumulative capture up to pre-auth ceiling
Given an order with a pre-authorization ceiling and items split across multiple bags When the first bag is handed off and marked accepted Then the system captures only the accepted bag amount (with promotions, tax, and allocated tip) and updates cumulative_captured not to exceed the pre-authorization amount And the order remains open for remaining items with payment_state set to Partially Captured When subsequent bags are handed off Then additional captures occur cumulatively without exceeding the pre-authorization And on the final bag, the order payment_state is set to Paid and cumulative_captured equals the final accepted total And if a bag’s accepted items would exceed remaining pre-auth balance, the system blocks that capture, prompts for remediation (refresh auth or alternate payment), and does not exceed the ceiling
Accepted-items-only capture after item changes before handoff
Given items were removed, substituted, or quantities changed prior to handoff And discounts and taxes are recalculated based on accepted items When the Handoff event is triggered Then the capture amount equals the recalculated totals for accepted items only And the receipt and payment records reflect only accepted items, updated discounts, taxes, and tip And no payment is captured for declined or removed items
Idempotent capture prevents duplicates on retries and duplicate events
Given an idempotency key is generated for the order handoff event When the Handoff event is retried due to network error or a duplicate event is received within the idempotency window Then at most one successful capture is executed for that idempotency key And subsequent identical requests return a success acknowledgement referencing the original capture ID without charging again And audit logs show a single capture with deduplicated retries recorded
Capture failure alerts staff with guided remediation without blocking lane
Given the processor returns a failure (e.g., decline, expired authorization, network timeout) When the Handoff event attempts capture Then the system displays a banner alert to staff within 3 seconds with failure reason and remediation options: Retry capture, Refresh/increase authorization, Alternate payment method, or Mark for back-office review And the lane workflow remains actionable and the current order is flagged Needs Payment without auto-cancellation And no receipt is sent, and a failure log with error code and correlation ID is recorded And selecting Retry uses the same idempotency key; selecting Alternate payment launches tender selection and on success links the alternate payment to the order
Digital SMS receipt content and delivery on successful capture
Given a successful capture occurs When the system sends the SMS receipt Then the SMS includes merchant name, order number, pickup bay, itemized accepted items with quantities, discounts, taxes, tip, total captured, last4 of card, and a link to a full digital receipt And the SMS is queued within 2 seconds and delivery status is tracked; failures are retried up to 3 times And the digital receipt URL is accessible for at least 30 days and matches the captured amounts
All handoff triggers initiate the same capture engine
Given the order can be completed by runner confirmation, bay QR scan, or one-tap complete in the Captain UI When any of these handoff triggers is activated for an order with an active pre-authorization Then the same capture engine is invoked with identical calculation rules and idempotency handling And the results (capture, receipt, logs, status updates) are consistent across trigger types
Real-Time Adjustment and Repricing
"As a curbside captain, I want to adjust the order and see updated totals before capture so that the guest is charged accurately without needing refunds later."
Description

Enable staff to edit items, quantities, and substitutions after arrival but before capture, with instant recalculation of subtotal, tax, discounts, service fees, and tip. Display a clear delta from the pre-auth amount and warn if the final exceeds the available pre-auth buffer, prompting a top-up authorization via SMS link. Ensure tax rules and menus are applied from the correct store context and time. Lock edits at the moment of capture to maintain consistency and produce an auditable final invoice.

Acceptance Criteria
Pre-Capture Item Edit Recalculates Totals and Delta
Given an arrived order with a pre-authorization amount A and available buffer B When a staff member edits items (add/remove/substitute) or quantities before capture Then the system recalculates subtotal, tax, discounts, service fees, and tip within 1 second of each saved change And the UI displays the new total T, the pre-auth amount A, and a delta (T − A) with currency and +/- sign And rounding rules and discount applications follow the store’s configured policy And no payment is captured until the user explicitly initiates Capture
Exceeding Pre-Auth Buffer Triggers SMS Top-Up
Given an order with authorized amount A and buffer B (available = A + B) When the recalculated total T exceeds available Then a blocking warning displays the shortfall amount (T − available) to the nearest cent And an SMS link to authorize the shortfall (plus any configured top-up margin) is sent to the guest within 5 seconds And the Capture action is disabled until the top-up is approved or T is reduced to ≤ available And upon top-up approval, available updates and the warning clears within 2 seconds And upon decline or timeout, the event is logged and staff are offered retry and adjust options while Capture remains disabled
Correct Store Context and Time Applied to Pricing and Tax
Given the order is associated to Store S and arrival timestamp Ts When totals are recalculated after edits Then menu prices, discounts, tax rules, and service-fee configuration effective for Store S at Ts are applied And jurisdictional taxability (item-level, inclusive/exclusive, gratuity tax rules) is correctly enforced And if store context is missing or stale, recalculation and capture are blocked with a prompt to select the correct store And the invoice persists Store S identifier, tax jurisdiction, and ruleset/version used
Capture Locks Edits and Ensures Consistent Finalization
Given an order in an editable state with pending recalculations When a user initiates Capture Then the system atomically locks the order, finalizes totals, and prevents further edits And any unsaved edits in other sessions are rejected with a clear message to refresh And the captured amount equals the finalized total and references the pre-auth and any top-ups And repeated Capture attempts are idempotent and do not create duplicate charges And post-capture views are read-only with an explanation that edits are locked
Concurrent Edit Conflict Handling
Given two staff members have the same arrived order open When both attempt to save edits before capture Then the system enforces optimistic concurrency with version checks And the first save succeeds; the second receives a conflict notice including a summary of differences since load And the second user can review, optionally merge, and resubmit; no silent overwrite occurs
Auditable Final Invoice and Change Log
Given an order that has been edited and captured When viewing or exporting the final invoice Then the invoice lists line items, quantities, unit prices, discounts, taxes, service fees, tip, and total And includes a change log of edits (timestamp, user, field, before→after values) and calculation snapshots And references payment authorizations and captures (IDs, amounts, timestamps) and the store/tax ruleset used And the invoice is immutable after capture and exportable as PDF and JSON with a verifiable checksum/signature
Payment Failover and Retry Queue
"As an operator, I want captures to recover automatically from transient failures so that curbside flow isn’t interrupted and payments still settle reliably."
Description

Provide resilient handling for gateway timeouts, network loss, or gateway outages by queuing capture requests with idempotent keys and exponential backoff. Surface countdowns for pre-auth expiration and attempt smart re-auth if expirations occur during wait. Offer a manual fallback flow to accept offline signatures or alternate tender with appropriate audit flags. Ensure the curb lane remains unblocked by decoupling capture from UI responsiveness while guaranteeing eventual consistency and clear staff alerts for exceptions.

Acceptance Criteria
Gateway Timeout Retry with Idempotent Capture
- Given an order with a valid pre-authorization and a capture attempt returns a retryable error (network loss, 408, 429, 500–504), When the system enqueues the capture with idempotency key {orderId}:capture:{version}, Then the gateway processes at most one successful capture and duplicate attempts return the same transactionId. - Given a queued capture, When retries occur, Then delays follow exponential backoff 2s, 4s, 8s, 16s, 32s, 64s, 128s with ±20% jitter and a max delay cap of 300s, up to a maximum of 7 attempts. - Given a queued capture, When a non-retryable response is received (400, 401, 403, 404, 422, or 402 Payment Required/decline), Then the job stops retrying and the order transitions to Requires Action with the terminal reason recorded. - Given the payment worker restarts, When the service comes back up, Then the retry queue restores pending jobs within 5s and resumes without duplicating any in-flight job. - Given multiple captures for different orders, When the worker runs, Then no more than 10 jobs execute concurrently across the tenant and no more than 1 job per order executes concurrently. - Given a successful capture, When the gateway confirmation is received, Then the job is removed from the queue, the order state becomes Settled, and no further retries occur.
UI Remains Responsive When Capture Is Pending
- Given a runner taps Hand Off during degraded or lost connectivity, When the capture request is enqueued, Then the UI transitions the order to Released within 100ms and the curb lane list remains fully interactive. - Given an order with a pending capture, When staff navigate to other orders, Then no blocking modal or spinner prevents other actions and bay assignment operations remain under 150ms P95. - Given connectivity resumes, When the queued capture is attempted, Then on success the guest receipt is sent within 30s and the order visually updates to Paid. - Given the capture ultimately fails after max retries, When the order requires manual resolution, Then a banner with CTA "Take manual payment" appears within 5s and persists until resolved.
Pre-Auth Countdown and Smart Re-Auth on Expiration
- Given an order with an active pre-authorization, When viewed in Captain UI, Then a mm:ss countdown to auth expiration is displayed and updates every 1s. - Given time to expiration is ≤ 5:00, When the order is viewed, Then the timer displays amber; and when ≤ 1:00, Then the timer displays red. - Given a queued capture and time to expiration is ≤ 0:30, When prioritization runs, Then the job is boosted to head-of-line for the merchant queue. - Given the pre-auth has expired before capture, When capture is initiated, Then the system performs a smart re-auth for the latest order total using the stored token and, on approval, captures immediately under a new idempotency key. - Given the latest total differs from the original pre-auth, When capture occurs, Then the captured amount equals the latest total; if the latest total exceeds the original pre-auth, a re-auth is performed prior to capture. - Given re-auth fails (decline/non-retryable), When capture cannot proceed, Then the order moves to Requires Action and the manual fallback entry point is presented.
Manual Fallback: Offline Signature or Alternate Tender
- Given an order in Requires Action, When staff selects Manual Fallback, Then the system offers options: Offline Signature, Alternate Tender: Cash, Alternate Tender: Card Present. - Given Offline Signature is selected, When the guest signs, Then the signature image, staff ID, timestamp, amount, and reason code are stored and the order state becomes Settled Offline with audit flag offline=true. - Given Alternate Tender is selected, When staff records the payment reference (cash drawer receipt ID or terminal reference), Then the order state becomes Settled Offline with tender type and reference saved in the audit log. - Given any manual fallback is completed, When a late gateway capture result arrives due to prior retries, Then the system auto-voids the late capture and appends the void reference to the order audit trail to prevent double charge. - Given the manual fallback amount exceeds $100, When staff attempts completion, Then a manager PIN is required to proceed.
Exception Alerts and Outage Handling
- Given multiple orders experience retryable capture errors, When ≥10 capture attempts across ≥5 orders fail with retryable codes within 60s, Then the system flags a suspected gateway outage, pauses new retries for 5 minutes, and displays a system banner "Gateway issue detected" in the Captain UI. - Given a suspected outage is flagged, When the error rate falls below the threshold for 2 consecutive minutes, Then retries automatically resume and the banner clears. - Given an individual order exhausts max retries, When the final attempt fails, Then the order is marked Requires Action and an alert card with CTA "Take manual payment" appears within 5s with the failure reason. - Given a suspected outage persists beyond 10 minutes, When escalation rules run, Then an SMS is sent to the configured manager number and an incident is recorded in the audit log.
Eventual Consistency and Payment Reconciliation
- Given successful captures, When the reconciler runs every 15 minutes, Then each internal capture record matches a gateway transaction by idempotency key or transactionId and internal state is Settled. - Given a mismatch or missing settlement is detected, When reconciliation runs, Then a Reconcile Task is opened within 5 minutes and the order displays a Requires Review badge to staff. - Given an order enters any payment flow, When 24 hours elapse, Then its financial state is one of Settled, Voided, Settled Offline, or Requires Action; no order remains in a transient state beyond 24 hours. - Given normal gateway conditions, When measured over a rolling 30-day window, Then 99% of successful captures reach Settled and notify staff within 2 minutes of the initial capture attempt (P99 latency SLO).
Compliance, Security, and Audit Trail
"As a finance lead, I want secure processing and detailed audit records so that we reduce disputes and can win chargebacks when they occur."
Description

Integrate via PCI-compliant tokenization with restricted scopes, never storing PAN or CVV. Support 3DS/SCA where required by region and card brand with a friction-minimized flow through SMS links or browser wallet. Record a complete, immutable audit trail of pre-auth, adjustments, capture attempts, actor identities, timestamps, bay/vehicle, and IP/device data. Generate a chargeback defense package including signed receipt, itemization, adjustment history, and handoff confirmation artifacts (e.g., bay scan, staff confirmation). Enforce least-privilege access and encrypt sensitive metadata at rest and in transit.

Acceptance Criteria
PCI Tokenization and Restricted Scope Integration
- Given a guest enters payment via hosted fields or browser wallet When pre-auth is submitted Then only a processor token and non-sensitive card metadata (brand, last4, expiry) are stored; PAN/CVV are never persisted in databases, caches, or logs. - Given automated DLP scanners run daily on production storage and logs When scans complete Then zero PAN/CVV patterns are found; any blocked attempt generates a sev-High alert within 5 minutes. - Given payment API keys with restricted scopes When an operation outside scope is attempted Then the gateway returns 403/permission error and an audit event with actor, IP, and scope is recorded.
Regional SCA/3DS Enforcement with Friction-Minimized Flow
- Given region/card brand requires SCA When guest initiates pre-auth via SMS link Then a 3DS challenge or wallet cryptogram is invoked and returns to CurbPing within 10 seconds after success, capturing liability shift details. - Given SCA is not required or an exemption applies When pre-auth is initiated Then no challenge is presented and pre-auth proceeds. - Given SCA challenge fails or times out When guest returns Then pre-auth is not created; UI offers retry/alternate payment; audit logs include reason code and outcome.
Immutable Audit Trail from Pre-Auth to Capture
- Given pre-auth creation, adjustments, and capture attempts occur When events are recorded Then each entry includes order ID, event type, actor ID and role, UTC timestamp, bay number, vehicle descriptor, IP, device/user-agent, previous amount, new amount, reason code, and processor response codes as applicable. - Given audit entries are appended When integrity is verified Then each entry contains prev_hash and current_hash; any mutation attempt is rejected and a security alert is generated. - Given a failed capture followed by retry When queried Then audit shows each attempt with distinct timestamps and outcomes.
Chargeback Defense Package Generation and Delivery
- Given an authorized Manager requests a defense package for an order When generated Then it includes signed receipt, itemized final bill, adjustment history, SCA/3DS evidence, audit trail, handoff artifacts (bay scan or staff confirmation), IP/device data, and bay/location metadata within 60 seconds. - Given the package is prepared When downloaded or transmitted to the processor Then PAN/CVV are absent; token and last4 only; files are cryptographically signed and a checksum is provided. - Given an unauthorized user requests the package When processed Then access is denied with 403 and the attempt is logged.
Least-Privilege RBAC and Data Redaction
- Given staff roles (Runner, Captain, Manager) When accessing payment and audit data Then permissions enforce least privilege: Runners cannot view payment tokens or audit artifacts; Captains can view order totals and bay/vehicle only; Managers can export defense packages. - Given an API client scoped to payments:capture When it requests audit logs or token lists Then the request is denied with 403 and logged. - Given UI renders sensitive fields When displayed Then IPs, device fingerprints, and emails/phones are masked per policy for non-manager roles and never expose PAN/CVV.
Encryption In Transit and At Rest for Sensitive Metadata
- Given network communication of tokens, audit entries, or artifacts When requests are made Then TLS 1.2+ with HSTS and modern ciphers is enforced; plaintext HTTP is redirected or blocked. - Given storage of audit logs and artifacts When persisted Then sensitive fields are encrypted at rest with KMS-managed AES-256-GCM keys; key rotation occurs at least every 90 days; access is logged in KMS. - Given database backups and exports When created and restored Then remain encrypted; decryption keys are limited to least-privilege roles and access attempts are logged.
Arrival and Handoff Context Captured in Audit
- Given the guest clicks the SMS 'I'm here' link and is assigned a bay When pre-auth is created Then the audit trail records bay number, vehicle descriptor, IP address, device/user-agent, and geotag (if available). - Given staff completes handoff at the bay When capture is executed Then the audit includes staff user ID and role, UTC timestamp, bay confirmation artifact (QR/scan/code entry), and final captured amount. - Given item changes occur before handoff When capture is executed Then final totals in the audit reflect adjustments with reason codes, and the guest-visible receipt matches the captured amount.
Curb Console Payment States and Guardrails
"As a shift lead, I want clear payment statuses and guided actions in the console so that my team can resolve issues fast and keep curbside throughput high."
Description

Add clear payment state indicators in the staff console (Not Authorized, Pre-Authorized, Adjustment Needed, Capturing, Captured, Failed) with role-based actions. Provide guided prompts to obtain pre-auth, request top-ups, or resolve failures without leaving the lane view. Prevent handoff completion when no valid pre-auth exists (configurable override with reason code). Offer quick-access SMS resend for payment links and real-time guest status visibility to minimize back-and-forth and keep lanes moving.

Acceptance Criteria
Payment State Indicators and Transitions in Lane View
Given an order with no payment events When the order appears in the lane view Then the payment state label displays "Not Authorized" Given an order after successful pre-authorization When the staff views the lane card Then the payment state label displays "Pre-Authorized" and shows the authorized amount and timestamp Given a pre-authorized order When items are added or removed before capture and the new total exceeds the authorized amount Then the payment state label displays "Adjustment Needed" and shows the additional amount required Given a pre-authorized order When items are removed before capture and the new total is less than or equal to the authorized amount Then the payment state remains "Pre-Authorized" and the capture amount updates to the new total Given a capture is initiated from the lane view When the request is sent to the payment processor Then the payment state displays "Capturing" until a success or failure is returned Given the processor returns success When the capture completes Then the state displays "Captured" and the captured amount matches the order total Given the processor returns error or decline When the capture attempt completes Then the state displays "Failed" and shows the processor error code/message Given any of the above events occur When the state changes Then the lane view updates within 2 seconds without page reload
Role-Based Permissions for Payment Actions
Given I am logged in as Runner When viewing an order Then Pre-Authorize, Request Top-Up, Capture, and Override Handoff controls are disabled with tooltip "Insufficient permissions" and Resend Payment Link is enabled Given I am logged in as Captain When viewing an order Then I can Pre-Authorize, Request Top-Up, Capture, and Resend Payment Link, and I cannot Override Handoff without valid pre-auth Given I am logged in as Manager When viewing an order Then I can perform all payment actions including Override Handoff Given a user without a mapped role When viewing the console Then all payment action controls are hidden Given role permissions are updated by an admin When I refresh the console Then the updated permissions are enforced on the next session
Guided Prompts for Pre-Auth, Top-Up, and Failure Resolution
Given I click Pre-Authorize on an eligible order When the prompt opens Then it appears as an in-lane modal or side panel pre-populated with the order total and a Send Payment Link action Given I click Request Top-Up on an order in "Adjustment Needed" When the prompt opens Then it shows the additional amount required and allows editing up to the new total with inline validation Given I click Resolve on an order in "Failed" When the prompt opens Then it displays the failure reason and offers actions: Retry Capture, Send New Payment Link, or Switch Payment Method (if enabled) Given any prompt is open When I complete the action successfully Then the prompt closes and the lane card payment state updates within 2 seconds Given any prompt is open When I cancel Then I remain in the lane view with no navigation and no changes applied Given validation errors occur When I submit Then inline error messages display and the primary action remains disabled until errors are resolved
Handoff Completion Guardrail and Configurable Override
Given an order without a valid pre-authorization When a user attempts to mark Handoff Complete Then the system blocks completion and shows a banner "Payment not authorized. Obtain pre-auth or override." Given override is disabled in configuration When a user attempts to override Then the Override option is not displayed Given override is enabled and I am a Manager When I choose Override Then I must select a required reason code and confirm with manager PIN/SSO before completion Given an override is used When Handoff completes Then the system logs order ID, user ID, role, timestamp, and reason code to the audit log Given an order with a valid pre-authorization When a user marks Handoff Complete Then the handoff proceeds and capture is initiated
Quick-Access Resend Payment Link via SMS
Given an order in "Not Authorized" or "Adjustment Needed" When I click Resend Payment Link Then an SMS is sent to the guest's phone on file and the UI shows "Link sent" with a timestamp Given I attempt to resend within 60 seconds of the last send When I click Resend Payment Link Then the action is blocked and a message displays "Please wait 60 seconds before resending." Given the SMS provider returns a delivery receipt When it is received Then the lane card updates the guest status to "Delivered" Given the guest opens the link When the open event is received Then the lane card updates the guest status to "Opened"
Real-Time Guest Payment Status Visibility
Given the guest opens the payment link, enters card details, or approves pre-authorization When these events occur Then corresponding status chips update in the lane card within 2 seconds Given a pre-authorization is approved When approval is received Then the lane card shows "Pre-Authorized" with the authorized amount and masked card (e.g., ****1234) Given a top-up is requested When it is sent Then the lane card displays "Top-up requested" with amount and time; when approved, it updates to "Pre-Authorized" with the new aggregate amount Given the lane view loses connectivity When live updates cannot be received Then a "Reconnecting…" indicator appears and statuses resume updating automatically upon reconnection
Order Adjustment Recalculation and Capture Amount Integrity
Given a pre-authorized order When items change before handoff Then the order total recalculates within 1 second and rounds per currency rules Given the new total is less than or equal to the authorized amount When capture is initiated Then the captured amount equals the new total without requesting additional authorization Given the new total exceeds the authorized amount When capture is initiated Then capture is blocked and the state remains "Adjustment Needed" until sufficient pre-authorization is obtained Given multiple adjustments occur before capture When the final total is set Then only the final amount is captured and any superseded top-up requests are voided Given a top-up is approved When authorization holds exist Then the aggregate authorized amount meets or exceeds the final total prior to capture

Instant Adjust

Fix mistakes on the spot—partial refunds, price adjustments, or comps—directly from the SMS thread. Updated receipts send instantly, preserving goodwill and speed without ever touching the POS.

Requirements

Unified Adjustment Engine (Item- and Order-Level)
"As a shift lead, I want to apply partial refunds, price adjustments, or comps at item or order level so that I can fix mistakes quickly without touching the POS."
Description

Provide a backend service and UI controls to create and apply partial refunds, price reductions, and complimentary items/fees at both item and order level, without invoking the POS. Support selecting affected line items, adjusting quantities, overriding unit price or discount percentage, and comping fees (delivery, service) while preserving original order context. Automatically recalculate taxes and tips according to jurisdictional rules and merchant configuration, ensure adjustment reason capture, and update order state atomically.

Acceptance Criteria
Item-Level Unit Price Override with Tax Recalculation
Given an open order with a taxable line item and current jurisdictional tax configuration When the agent selects that line item and overrides the unit price from 12.00 to 10.00 Then the system recalculates the line subtotal, applicable taxes, and order totals using the jurisdictional rules and merchant rounding configuration And the original unit price and quantity are preserved in the immutable order context with before/after values captured And a required adjustment reason is selected or entered before Save is enabled And the change is persisted atomically as a new order version and returned in a single success payload And no POS API calls are invoked during this operation (verified by integration logs) And the updated receipt reflects the new unit price, tax, and totals with a “price adjusted” annotation on the line
Order-Level Percentage Discount with Tip Handling per Merchant Setting
Given an open order with pre-discount subtotal, tax, and a tip configured per merchant settings When the agent applies a 15% order-level discount Then the system recalculates taxable amounts per jurisdictional rule (pre- or post-discount taxation) and updates tax accordingly And tip is recalculated or preserved according to merchant configuration (tip-on-pre-discount or tip-on-post-discount) And totals are rounded per merchant rounding policy and match the sum of parts And a required adjustment reason is provided; otherwise Save remains disabled And no POS API calls are made; POS state remains unchanged And the receipt summary shows an “Order Discount 15%” line and updated totals
Partial Quantity Refund on Single Line Item
Given an open order with a line item quantity of 3 and applicable tax When the agent refunds quantity 1 for that line item Then the system creates an adjustment that reduces the billable quantity to 2 and computes the refunded amount including proportional tax And the original line item context is preserved and the refund is represented as a separate adjustment record or split line per design And the order state transitions to Partially Refunded and totals reflect the refund immediately And a reason is required for the refund before Save is enabled And no POS API calls are made And the customer receipt shows the refunded quantity and amount with a “Refunded x1” annotation
Comp Delivery and Service Fees
Given an order with a delivery fee and a taxable service fee When the agent marks the delivery fee as Comped Then the delivery fee amount becomes 0.00 and is flagged as Comped in calculations and on the receipt And taxes that include the comped fee are recalculated per jurisdictional rules And the service fee remains unchanged unless explicitly comped And a reason is required; otherwise Save is disabled And the order totals update immediately and rounding rules are applied And no POS API calls are made during the comp And the receipt displays “Delivery Fee (Comped)” and updated totals
Adjustment Reason Capture and Audit Trail
Given any adjustment (price override, discount, refund, or comp) When the agent attempts to Save without selecting a reason code or entering a free-form note (if configured) Then the Save action is blocked and a validation message explains the requirement When the agent provides a valid reason and saves Then the audit log records staff identity, timestamp, workstation/session, reason code, optional note, and before/after financial deltas And the audit record is immutable and exportable via reporting endpoints And the reason appears on the internal order timeline and, if merchant-enabled, as a brief note on the customer receipt And no POS API calls are made
Atomic Update and Concurrency Control
Given two staff members open the same order at version v10 When Staff A saves an adjustment successfully to create version v11 Then Staff B’s subsequent Save against v10 is rejected with a clear concurrency error and no changes are applied And Staff B is prompted to reload the latest order state before retrying And at no time is a partial adjustment state observable to other clients And all affected totals, taxes, tips, and receipts correspond exactly to a single committed version And only one SMS receipt update is sent for the committed adjustment And no POS API calls occur during either attempt
Instant Receipt Regeneration and SMS Delivery without POS Invocation
Given an adjustment is saved successfully When the system commits the new order version Then a new receipt is generated reflecting all changes and a unique, short link is sent via SMS to the customer within 3 seconds And the previous receipt link redirects to the latest version to avoid stale totals And delivery status of the SMS is logged; on failure, the UI surfaces a non-blocking alert with a “Resend” option And the receipt clearly itemizes adjustments (e.g., price overrides, refunds, comps) and updated taxes/tips And no POS API calls are made for this process And all events (adjustment, receipt regeneration, SMS send) are recorded in the order timeline
Payment Gateway Connectors with Idempotent Refunds
"As an operator, I want refunds to process through our existing payment provider automatically so that adjustments are seamless and accurate."
Description

Build an abstraction layer over supported payment providers (e.g., Stripe, Square, Toast, Clover) to execute partial refunds and adjustments using original transaction tokens. Implement idempotency keys, retries, and standardized error mapping. Store gateway response metadata and associate it to the order for traceability. Support multi-capture scenarios, tip adjustments where allowed by the provider, and fallback flows when a provider does not support specific adjustment types.

Acceptance Criteria
Partial Refund with Idempotency and Retry (Stripe)
Given an order has a valid Stripe transaction token and a requested partial refund amount <= captured amount When a partial refund is initiated from Instant Adjust Then the connector sends a refund request with an idempotency key derived from {orderId, provider, originalChargeId, amount, reason} And then on 5xx or network timeouts it retries up to 3 times with exponential backoff (e.g., 200ms, 800ms, 1600ms) And then duplicate requests using the same idempotency key return the same Stripe refundId without creating additional refunds And then the order timeline stores refund amount, currency, gatewayRefundId, idempotencyKey, requestedAt, and status
Tip Adjustment Where Provider Permits (Square)
Given Square allows post-authorization tip adjustments within a provider-defined window and the order capture is eligible When a tip adjustment of X is requested Then the connector submits a tip adjustment call to Square and receives success And then the final captured total equals original captured total + X And then the adjustment is recorded with adjustmentType=tip and includes gatewayChargeId, gatewayAdjustmentId, amounts, timestamps And then attempts outside the window or exceeding limits return normalized codes {ADJUSTMENT_WINDOW_EXPIRED, LIMIT_EXCEEDED} with retryable=false
Standardized Error Mapping and Surfacing
Given any provider returns an error to a refund/adjustment request When the connector processes the error Then it maps the error to a normalized structure {code, severity, retryable, messageKey, fieldPaths?} And then transient errors (5xx, timeouts, rate limits) are marked retryable=true; validation/business errors are retryable=false And then the mapped error is persisted on the order event and visible to staff with messageKey within 500 ms And then raw provider codes/messages are stored in metadata for traceability
Multi-Capture Partial Refund Allocation (Toast)
Given an order has multiple captures on Toast totaling >= the requested refund amount When a partial refund is requested for amount A Then the connector allocates refunds FIFO across captures until A is fully satisfied And then individual refund requests are created per capture as required by Toast And then the sum of refunded amounts equals A and no capture is over-refunded And then each per-capture refundId, captureId, and refundedAmount is stored on the order
Fallback for Unsupported Adjustment Types (Clover)
Given Clover does not support the requested adjustment type for the transaction When an adjustment is requested Then the connector blocks the unsupported operation and returns normalized error UNSUPPORTED_OPERATION And then it returns a fallbackOptions array (e.g., FULL_REFUND_AND_RECHARGE, STORE_CREDIT_NOTE) with necessary parameters And then the selected fallback, if executed, is recorded with fallbackType, linkage to any new payment, and actorId
Persist Gateway Metadata and Audit Trail
Given any refund or adjustment request succeeds or fails When the provider response is received Then the system persists request/response metadata per schema: gatewayName, gatewayChargeId, gatewayRefundId/gatewayAdjustmentId, status, rawCode, rawMessage, normalizedCode, idempotencyKey, providerRequestId, timestamps, amounts, currency, actorId And then entries are immutable and versioned, retrievable via order timeline API and export endpoints And then sensitive data (PAN, full cardholder name, CVV) is never logged or stored; logs contain only tokens and correlation ids
Instant Receipt Regeneration and SMS Delivery
"As a customer, I want to receive an updated receipt right away so that I can see exactly what changed and trust the resolution."
Description

Regenerate an itemized receipt reflecting all adjustments with clear before/after amounts, taxes, tips, and the adjustment reason. Host the updated receipt at a secure short link and send it instantly via the existing SMS thread; optionally send via email if on file. Include merchant branding, timestamp, order/bay identifiers, and a unique receipt version number. Ensure the receipt updates in real time if additional adjustments are made.

Acceptance Criteria
Accurate Itemized Receipt Regeneration
Given an order with one or more adjustments (partial refund, price change, or comp) and a recorded reason When staff regenerates the receipt Then the receipt displays for each affected line item: original amount, adjusted amount, and delta, using the merchant locale’s currency format And the receipt totals show subtotal, tax, tip, and grand total both before and after adjustments, with tax recalculated per current tax rules and rounded to two decimals And the receipt includes merchant branding (logo and name), order ID, bay number (if assigned), timestamp in merchant timezone, and a unique monotonically increasing receipt version number starting at 1 And the tip amount remains unchanged unless the adjustment explicitly modifies tip And no POS system is modified during this operation
Secure Hosted Short Link
Given a regenerated receipt When a short link is created Then the link uses HTTPS and a signed, non-guessable token with at least 128 bits of entropy And the link contains no PII or pricing data in the path or query parameters And the link resolves to the latest receipt version within 2 seconds and returns HTTP 200 And an invalid or expired token returns HTTP 404 without revealing order existence And the receipt page sets noindex and nosnippet and requires no authentication to view with the tokenized URL And the short link remains valid for at least 90 days
Instant SMS Receipt Delivery
Given there is an existing order SMS thread with the customer When staff regenerates the receipt Then an SMS containing the short link is queued within 2 seconds and delivered in the existing thread within 60 seconds where carrier receipts are available And the SMS body includes the merchant name, order ID, and receipt version number And if delivery is not confirmed within 60 seconds, a single retry is attempted via the same channel And no more than one SMS is sent per regeneration action And the customer receives a single clickable URL that opens the receipt successfully
Optional Email Receipt Delivery
Given the customer's email is on file and staff selects 'Also email receipt' When the receipt is regenerated Then an email is sent within 30 seconds with a subject containing the merchant name and order ID and a body link to the receipt And the email passes SPF and DKIM alignment checks And the email renders correctly on mobile and desktop and includes the receipt version number And if no email is on file or staff does not select the option, no email is sent
Real-time Receipt Auto-update
Given the receipt page is open by the customer or staff When an additional adjustment is saved Then the short link resolves to the newest version within 2 seconds and the visible receipt auto-updates within 3 seconds without a full page reload And the receipt version number increments by 1 and is displayed And a change banner briefly summarizes the new adjustments and deltas And no duplicate SMS is sent unless staff explicitly chooses to resend
Authorization and Audit Trail
Given a staff user attempts to regenerate and send an updated receipt When the user lacks the 'Adjustments:Issue' permission Then the action is blocked and no message or link is sent And when the user has the permission and proceeds Then an audit log entry is created capturing user ID, order ID, previous totals, new totals, adjustment reason, timestamp, delivery channels used, and receipt version number And the audit log can be exported and filtered by date, user, and order ID
Branding, Accessibility, and Performance
Given a regenerated receipt is viewed on a modern mobile browser When the page loads Then it fully renders in under 2 seconds on a 4G connection (≥750 kbps) with a total payload under 500 KB And the receipt adheres to WCAG 2.1 AA color contrast and displays merchant logo and colors And monetary values are right-aligned in a tabular layout and formatted per locale And the page prints to a single A4/Letter-friendly page without truncation
Role-Based Access Controls and Audit Trail
"As a GM, I want controls and visibility over adjustments so that we prevent abuse and meet audit requirements."
Description

Enforce permissions for who can create, approve, or revoke adjustments with configurable limits per role, shift, and location. Require reason codes and optional manager approval above thresholds. Log all events (who, what, when, before/after values, gateway refs, IP/device) in an immutable audit log searchable in the dashboard and exportable for compliance.

Acceptance Criteria
Crew Role Create Limits by Location and Shift
Given a user with role "Crew" assigned to Location A and Shift 2 And a per-role create limit of $15 for Shift 2 at Location A And no assignment to Location B When the user attempts to create a $10 adjustment from the SMS thread for an order at Location A during Shift 2 Then the system allows the adjustment without requiring approval And the user's remaining Shift 2 adjustment limit reflects a $10 deduction When the same user attempts to create a $20 adjustment at Location A during Shift 2 Then the system blocks creation and prompts for manager approval When the same user attempts to create any adjustment for an order at Location B Then the system denies access with "Insufficient permissions"
Manager Approval Above Thresholds and Multi-Role Escalation
Given location thresholds are configured: Crew <= $15, Shift Lead <= $50, Manager unlimited And Manager M1 is available to approve When a Shift Lead requests a $60 adjustment from the SMS thread Then the system routes an approval request to Manager M1 And the adjustment remains pending and cannot be applied until approved When Manager M1 approves Then the adjustment is applied and an updated receipt is sent to the customer via SMS When the approval is rejected Then the adjustment is canceled and the requester is notified
Reason Codes Required for Create, Approve, and Revoke
Given reason codes are configured as ["Wrong item", "Order delay", "Customer complaint", "Other"] When any user creates an adjustment Then the system requires selection of a reason code before submission And if "Other" is selected, a non-empty free-text explanation is required When a manager approves or rejects an adjustment, or a user with permission revokes an adjustment Then the system requires a reason code for that action as well
Revocation Permissions and Effects
Given only roles "Manager" and "Shift Lead" have revoke permission at Location A And an adjustment has been applied to Order #123 by a Crew member When a Shift Lead at Location A attempts to revoke the adjustment from the SMS thread Then the system allows revocation And a reversal is sent to the payment gateway using the original gateway reference And the customer receives an updated receipt via SMS When a Crew member attempts to revoke the same adjustment Then the system denies the action with "Insufficient permissions"
Audit Log Entry Completeness per Event
Given the audit log is enabled and immutable When an adjustment is created, approved, rejected, applied, or revoked Then an append-only entry is created for each event containing: - actor user ID and role - action type (create, approve, reject, apply, revoke) - timestamp in ISO 8601 with timezone - order ID and location ID - before and after values (amount, tax, total) - reason code and free-text notes (if any) - payment gateway reference(s) and response codes - source IP address and device/user-agent - request origin (SMS thread, dashboard, API)
Audit Log Immutability, Search, and Export for Compliance
Given the audit log is stored in an append-only data store with write-once semantics When any user attempts to edit or delete an existing audit entry Then the system blocks the action and records a security event When an auditor searches the dashboard by date range, role, user, location, order ID, action type, and amount range Then results are returned with accurate filtering When the auditor exports the search results Then CSV and JSON exports are available for download And the export includes column headers, UTF-8 encoding, and a SHA-256 checksum file of the export And time fields are in ISO 8601 with timezone And multi-location data includes a location ID column
SMS-Thread Action UX and Quick Templates
"As a curbside runner, I want quick actions and clear messages within the SMS thread so that I can resolve issues without switching tools."
Description

Embed adjustment actions directly in the staff console tied to the active SMS thread, enabling one-tap selection of items and prebuilt message templates that explain the adjustment. Support customizable templates, insertion of dynamic fields (order total, item name, adjustment amount), and optional customer confirmation steps for price changes before execution.

Acceptance Criteria
One-Tap Item Selection and Adjustment in SMS Thread
Given a staff user is viewing an active customer SMS thread in the CurbPing console When the user opens the Actions panel for adjustments Then the order’s line items are displayed with one-tap selection controls When the user selects one or more items and chooses an adjustment type (partial refund, price adjustment, comp) And enters an amount as currency or percentage Then the system validates the amount is non-negative and does not exceed the selected items’ subtotal And the Apply Adjustment action becomes enabled only when validation passes When the user confirms the adjustment Then the adjustment is applied and a system message is posted in-thread summarizing the change And the order total and affected item amounts update immediately in the UI
Apply Prebuilt Template with Dynamic Fields
Given prebuilt message templates exist and include dynamic fields {order_total}, {item_name}, and {adjustment_amount} When a staff user selects a template in the SMS composer from the thread’s Actions panel Then a preview shows the template with all fields resolved from the current order context and adjustment inputs When any required field cannot be resolved Then the Send action is disabled and an inline error identifies the missing field(s) When the user taps Send Then the message is posted to the customer SMS thread with the resolved values exactly matching the preview
Create, Edit, and Save Custom Templates
Given an authorized staff user opens Template settings from the staff console When the user creates a new template with allowed dynamic fields {order_total}, {item_name}, {adjustment_amount} and saves it Then the template appears in the template picker within the SMS thread Actions panel When the user edits an existing template and saves changes Then the updated template is available in the picker within 5 seconds without a page refresh And a default template can be set per adjustment type (partial refund, price adjustment, comp) and location
Customer Confirmation Flow for Price Changes
Given the customer confirmation option is enabled for price changes When a staff user initiates a price adjustment and provides amount and reason Then the system sends an SMS asking the customer to reply YES to approve or NO to decline, including {item_name}, {adjustment_amount}, and new {order_total} And the system waits up to 5 minutes for a reply When the customer replies YES within 5 minutes Then the adjustment executes automatically and the thread shows a success system message When the customer replies NO or no reply is received within 5 minutes Then the adjustment is not executed and the thread shows a canceled/expired message with a retry option for staff
Post-Execution Customer Message in SMS Thread
Given an adjustment has been executed on the order When the system notifies the customer Then an SMS is posted in the existing thread summarizing the adjustment with resolved {item_name}, {adjustment_amount}, and updated {order_total} And the message is sent within 10 seconds of execution for 95% of adjustments When message delivery fails Then the system retries up to 3 times over 2 minutes and surfaces a delivery failure banner to staff if unsuccessful
Audit Trail and Access Control for Adjustments
Given a user with adjustment permissions executes an adjustment When the adjustment completes Then the system logs user ID, timestamp, items adjusted, amounts, reason/template used, and any customer confirmation outcome in the order timeline And the log entry is viewable in the thread’s activity pane and exportable as CSV Given a user without adjustment permissions When they open the Actions panel Then adjustment controls are hidden or disabled with an explanatory tooltip, and no adjustment can be initiated
Concurrent Adjustment Handling
Given two staff members attempt to adjust the same order concurrently from the SMS thread When the first adjustment is executing Then the second adjustment is blocked with a clear in-UI message until the first completes And totals and remaining refundable amounts are recalculated immediately after completion When a queued or retried adjustment would exceed the remaining refundable amount Then the system prevents execution and displays an error explaining the limit
Risk Controls, Limits, and Fraud Checks
"As an owner, I want guardrails on adjustments so that we minimize loss while keeping service fast."
Description

Provide configurable daily and per-order caps, velocity checks, and reason validation to reduce fraud and mistakes. Flag suspicious patterns (high refund frequency, large comps) and require secondary approval. Display real-time warnings to staff and block disallowed operations based on gateway, order age, or tender type.

Acceptance Criteria
Per-Order Adjustment Cap Enforcement
Given the per-order cap for Instant Adjust is $20 And the order total is $50 When a staff user attempts a $21 adjustment (refund, price adjustment, or comp) from the SMS thread for order CP-123 Then the system blocks the adjustment And displays an inline warning: "Exceeds per-order cap ($20)" And prevents any receipt from being sent And records a security event with orderId, staffId, amount, cap, and timestamp Given the same configuration When a staff user attempts a $19 adjustment Then the system processes the adjustment successfully And sends an updated receipt to the customer within 5 seconds And decrements the daily cap by $19
Daily Adjustment Cap Enforcement
Given the location daily cap for Instant Adjust is $200 (resets at local midnight) And cumulative approved adjustments since local midnight equal $195 When a staff user attempts a $10 adjustment Then the system blocks the adjustment And displays an inline warning: "Daily cap remaining: $5" And records a blocked-attempt event with orderId, staffId, requestedAmount, remainingCap, and timestamp Given the same configuration When a staff user attempts a $5 adjustment Then the system processes the adjustment successfully And sets the remaining daily cap to $0 And subsequent adjustment requests the same day are blocked with the same warning Given the next day has started in the location’s timezone When a staff user opens Instant Adjust Then the remaining daily cap reflects the full configured amount
Velocity Check Requires Approval
Given velocity rules are configured as: - Per-staff: >3 adjustments within 10 minutes triggers Suspicious Velocity - Per-customer phone: >2 adjustments within 15 minutes triggers Suspicious Velocity When a staff user initiates a 4th adjustment within 10 minutes Then the system holds the adjustment in Pending Approval And displays: "Secondary approval required due to velocity" And notifies the Managers group via in-app alert And prevents processing until a manager approves or the request expires after 10 minutes Given the same rules When a 3rd adjustment is initiated for the same customer phone within 15 minutes Then the same Pending Approval flow is enforced
Reason Validation and Required Notes
Given a required reason list is configured: Wrong item, Missing item, Price correction, Service recovery, Other When a staff user submits an adjustment without selecting a reason Then the submission is blocked with: "Select a reason" Given the reason selected is Other When the free-text notes are fewer than 10 characters Then the submission is blocked with: "Provide at least 10 characters" Given the reason selected is Service recovery And the requested adjustment amount is greater than $10 When notes are fewer than 20 characters Then the submission is blocked with: "Provide at least 20 characters for service recovery over $10" Given a valid reason is selected and note requirements are met When the adjustment is submitted Then the system accepts the submission and records reason and notes in the adjustment record
Gateway/Order Age/Tender-Type Blocking
Given the payment gateway for the order is Gateway A And Gateway A allows partial refunds only within 30 days of authorization And the order authorization date is 31 days ago When a staff user attempts any refund Then the system blocks the action with: "Gateway A: refunds not allowed after 30 days" Given an order with tender type Cash When a staff user attempts a card refund Then the system blocks the action with: "Tender type not eligible for card refund" Given Gateway A allows a maximum of 1 partial refund per transaction And the order already has 1 partial refund When a staff user attempts an additional partial refund Then the system blocks the action with: "Gateway A: max partial refunds reached" And all blocked attempts are logged with orderId, staffId, reason, gateway, and timestamp
Real-Time Warnings and Remaining Limits Display
Given a staff user opens Instant Adjust for an order from the SMS thread When the user focuses the amount field or changes the amount Then the UI displays in real time: - Per-order cap value and whether the entered amount exceeds it - Remaining daily cap in dollars - Velocity status indicator (green <80% of threshold, amber 80–100%, red >100%) - Gateway, order-age, and tender-type constraints as badges And these values update within 2 seconds of any change And if the entered amount would exceed a cap, the primary action button is disabled with an explanatory tooltip
Secondary Approval Workflow and Audit Logging
Given secondary approval is required when any of the following are true: - Suspicious velocity is triggered - A comp or refund amount is ≥ $50 - The staff member has ≥5 adjustments approved in the past 60 minutes When a staff user submits such an adjustment Then the request moves to Pending Approval and is visible to users with role = Manager (not the requester) And a manager can Approve or Deny in-app after re-authentication And if approved within 10 minutes, the adjustment is executed immediately and the customer receives an updated receipt And if denied or not approved within 10 minutes, the request is canceled with an inline message to the requester And every step (request, approval/denial, execution) is written to an immutable audit log with orderId, requesterId, approverId, reason, amount, trigger type, IP/device, and timestamps
Reporting, Reconciliation, and Webhooks
"As an accountant, I want accurate adjustment data and exports so that I can reconcile payouts and financials."
Description

Surface adjustment metrics in reports (by location, staff, reason, type), provide CSV exports, and deliver signed webhooks to accounting, POS, or BI systems. Include receipt versioning, net impact on revenue and taxes, and daily settlement summaries aligned with gateway payouts. Offer APIs for partners to subscribe to adjustment events.

Acceptance Criteria
Adjustment Metrics Reporting by Dimensions
Given an admin selects a date range and filters by location(s), staff, reason, and type When they open the Adjustments report Then the report shows total count, gross adjusted amount, net revenue impact, and tax impact for the filtered selection to the cent And per-location, per-staff, per-reason, and per-type subtotals equal the overall totals when summed And applying or clearing any filter updates totals and row sets within 2 seconds And all timestamps display in the location’s local time zone And the same figures are returned by the reporting API for the identical filter set
Adjustments CSV Export Matches Filters
Given a user applies report filters and clicks Export CSV When the CSV is generated Then it is downloadable within 10 seconds for up to 50,000 rows And the file is UTF-8 encoded with a header row And the columns include: adjustment_id, event_type, order_id, location_id, staff_id, reason, type, original_total, adjusted_total, net_revenue_impact, tax_impact, currency, receipt_version, created_at_local, created_at_utc, settlement_date, gateway_batch_id And numeric fields use dot decimal notation with 2 fractional digits And the row count and all totals match the on-screen report for the same filters And date-time fields are ISO 8601 with timezone where applicable
Signed Webhooks for Adjustment Events
Given a partner has an active webhook subscription with a shared secret When an adjustment is created, updated, or reversed, or a settlement summary is ready Then the system POSTs an event (adjustment.created|adjustment.updated|adjustment.reversed|settlement.summary.ready) to the target URL within 5 seconds And the payload includes: event_id, event_type, occurred_at, adjustment_id, order_id, location_id, staff_id, reason, type, original_total, adjusted_total, net_revenue_impact, tax_impact, currency, receipt_version, settlement_date, gateway_batch_id And headers include X-CurbPing-Signature (HMAC-SHA256 of raw body), X-CurbPing-Timestamp (epoch seconds), and X-Idempotency-Key And deliveries are considered successful only on 2xx response; 5xx and 429 are retried with exponential backoff up to 16 attempts over 24 hours; non-429 4xx are not retried And delivery attempts and outcomes are visible in an admin log with request/response metadata (excluding secrets)
Daily Settlement Summary Aligned With Gateway Payouts
Given a payment gateway batch closes When the next daily settlement process runs (by 06:00 local time) Then a settlement summary is generated per batch with fields: batch_id, date_range, gross_sales, discounts, comps, price_adjustments, partial_refunds, total_adjustments, tips, taxes, fees, net_revenue_impact, net_payout And the sum of adjustments by type equals total_adjustments And the computed net_payout matches the gateway payout amount within $0.01 for the batch And the summary references all included orders/adjustments and is downloadable as CSV and viewable in UI And any discrepancies beyond $0.01 are flagged with an alert and discrepancy report
Receipt Versioning and Audit Trail
Given an order has an issued receipt When an adjustment is applied via Instant Adjust Then a new immutable receipt version is created with a monotonically increasing integer version number starting at 1 And the latest SMS receipt link shows the newest version within 2 seconds And previous versions remain retrievable via an audit view and API And each version stores: version, timestamp, staff_id, reason, changes diff, totals (original vs new), tax deltas, and a cryptographic checksum And GET /receipts/{order_id}/versions returns an ordered list; GET /receipts/{order_id}/versions/{n} returns the exact version data/PDF matching what is shown in UI
Partner Subscription API for Adjustment Events
Given a partner with a valid API key and scope adjustments:write When they POST /api/v1/webhook-subscriptions with target_url, shared_secret, event_types, and optional location filters Then the API returns 201 with subscription_id, status=pending_verification, and echo of filters And the system performs a verification challenge to the target_url; upon successful challenge the status becomes active And GET lists subscriptions with pagination; PATCH can pause/resume; DELETE removes a subscription And unauthorized or scope-missing requests receive 401/403; invalid inputs receive 400 with field errors And API is rate-limited to 60 requests per minute per key with 429 responses including Retry-After
Webhook Delivery Idempotency, Retries, and Dead-Letter Handling
Given a webhook event is generated When deliveries are sent to a subscriber Then each attempt includes a unique delivery_id and an X-Idempotency-Key stable per event+subscriber And if the subscriber replays or duplicates the request, their 2xx response is still treated as success without additional side effects And failed deliveries are retried with exponential backoff up to 16 attempts over 24 hours; upon exhaustion, the event is moved to a dead-letter queue And admins can view and manually retry dead-lettered events from the UI And delivery latency p95 is under 5 seconds and success rate exceeds 99% when subscribers return 2xx

Link Lock

Device‑bound, time‑boxed payment links with OTP fallback and fraud screening. Links auto‑expire after pickup to prevent reuse, reducing chargebacks while keeping checkout effortless for good guests.

Requirements

Cryptographically Signed One-Time Link Tokens
"As a guest, I want a secure payment link that just works from my text so that I can check out quickly without creating an account and know my info is safe."
Description

Generate short payment URLs containing tamper-evident, time-bound tokens (e.g., HMAC/JWT) with order ID, phone hash, nonce, and expiry. Validate server-side with rotating signing keys and replay protection; store minimal state to allow single-use semantics and immediate invalidation on completion or pickup. Exclude PII from the URL, support key rotation, tolerate small clock skew, and apply per-link open rate limits. Integrate with SMS dispatch so each order receives a unique, signed link; ensure tokens cannot be guessed or reused across orders.

Acceptance Criteria
Token Composition and Tamper-Evident Signing
Given an order with an ID and customer phone When generating a payment link Then the URL contains a compact, URL-safe JWS token of length <= 256 characters And the token payload includes claims: oid (order ID), ph (salted phone hash), nce (nonce), exp (expiry epoch) And ph is a salted SHA-256 of the E.164 phone; no raw PII appears in the token or URL And nce has at least 128 bits of entropy from a CSPRNG and is unique per token And the token is signed with the active key (kid present) using HS256 or ES256 And any single-byte modification to the token causes server validation to return 401 Invalid Signature
Server Validation with Expiry and Clock Skew Tolerance
Given a presented token When server time is within exp ±120 seconds Then validation succeeds and returns 200 When the token is presented after exp+120 seconds Then validation fails with 410 Expired When the token has an invalid signature or unknown kid Then validation fails with 401 Invalid Signature And all validation attempts are recorded with reason code and token kid (no payload) within 100 ms
Single-Use and Replay Protection Under Concurrency
Given a valid token When it is redeemed the first time Then it is marked consumed and cannot be redeemed again When the same token is submitted concurrently two or more times within 1 second Then exactly one succeeds and all others fail with 403 Replayed When the order transitions to PaymentComplete or PickupArrived Then any unconsumed token for that order is invalidated within 1 second Persisted state per token is limited to {nonce, issuedAt, exp, consumedFlag, orderId} with TTL >= exp+24h
Signing Key Rotation and Backward Verification
Given active key K_n and previous key K_{n-1} When issuing new tokens Then kid=K_n is used When validating tokens signed with K_{n-1} within 72 hours of rotation Then they are accepted if otherwise valid When validating tokens signed with keys older than K_{n-1} Then they are rejected with 401 Unknown Key Key rotation occurs without downtime; issuing and validation succeed during the rotation window in integration tests
Unique Link Dispatch per Order via SMS
Given an order When the SMS is sent Then it contains a single HTTPS link with a unique signed token bound to that order ID When a link is re-sent for the same order Then a new token is generated and all prior unconsumed tokens for that order are invalidated within 1 second When attempting to redeem a token against a different order ID Then validation fails with 403 Order Mismatch For a batch of 10,000 orders, the collision rate of nonce values is 0 and no two tokens are identical
Per-Link Open Rate Limits
Given a token When the payment page is requested Then rate limiting enforces max 8 GET /open per 60 seconds per IP+UserAgent+token and max 25 total opens over the token lifetime When limits are exceeded Then the server responds 429 Too Many Requests with a Retry-After header Rate limiting does not block a single successful redemption within limits under load up to 95th percentile latency <= 200 ms
PII Exclusion and Safe Logging
Given end-to-end flows When URLs and server logs are captured Then no raw phone numbers, names, or emails appear in tokens, URLs, or logs Token values are redacted in logs after the first 8 characters; payload claims are not logged Security tests scanning 100,000 log lines find 0 occurrences of PII and 0 occurrences of full token values
Device Binding via Privacy-Preserving Fingerprint
"As a guest, I want my payment link to work on the device I first opened it on so that no one else can use it if they find the link."
Description

On first open, bind the payment link to the accessing device using a privacy-preserving device hash derived from stable, non-PII signals (e.g., user agent family, platform, secure random device key in storage, same-device cookie, and coarse IP subnet). Store only salted hashes; avoid storing raw fingerprints. On subsequent opens, compare the device hash to enforce same-device access; if mismatched or absent (private mode), trigger step-up verification. Provide graceful degradation when storage is disabled and ensure compatibility with major mobile browsers opened from SMS.

Acceptance Criteria
First Open Binds Link to Device
Given a customer opens the payment link from the SMS for the first time on a mobile browser When the page loads Then the system generates a secure random device key and persists it in Web Storage And sets a same-device cookie scoped to the link domain And derives a privacy-preserving device hash from normalized non-PII signals: user agent family, platform, device key, same-device cookie, and coarse IP subnet (IPv4 /24 or IPv6 /64) And salts and stores only the resulting hash and salt server-side And does not persist raw signal values beyond the current session And marks the link as bound to that device hash
Same-Device Reopen Allows Access Without OTP
Given the link is bound to a device hash And the customer reopens the link on the same device/browser with storage intact When the device hash is recomputed Then it matches the stored hash And the payment UI is accessible without OTP or additional steps And no new binding is created And the event is logged as "same_device_access"
Different Device Triggers Step-Up OTP Verification
Given the link is bound to device hash A When the link is opened from a device producing hash B ≠ A Then the payment UI is gated And a 6-digit SMS OTP challenge is sent to the order phone number And up to 5 attempts within 5 minutes are permitted And upon successful OTP entry, access is granted for the current session only And the original binding to hash A remains unchanged And the event is logged as "device_mismatch_step_up"
Private/Incognito or Cleared Storage Graceful Degradation
Given the customer opens the link in private/incognito mode or after storage/cookies are cleared When the device hash cannot include the device key or cookie Then the system derives a reduced-confidence hash from available non-PII signals (e.g., user agent family, platform, coarse IP subnet) And prompts for SMS OTP before granting access And does not attempt to persist new identifiers in private mode And binding to the original device remains unchanged And the event is logged as "degraded_binding_otp"
Storage Disabled Fallback Without Breakage
Given the browser denies access to Web Storage and cookies When the link is opened Then the page renders the OTP flow without uncaught JavaScript errors related to storage And access can be granted via OTP without enabling storage And functional navigation and payment UI are available post-OTP And the link's binding is not overwritten And Core Web Vitals remain within acceptable ranges (First Contentful Paint ≤ 2.5s on 3G simulated)
Hashing, Salting, and PII Compliance
Given device binding is executed When inspecting server storage and logs Then only salted device hash and non-reversible salts are stored And no raw signal values (full user agent string, IP address, cookie values, device key) are persisted beyond request scope or logged And no additional PII is collected for binding And salts are unique per link and generated with a CSPRNG And records are deleted no later than 30 days after link expiry
Mobile SMS Open Compatibility Across Major Browsers
Given the customer taps the SMS link When opened in iOS Safari, Android Chrome, Samsung Internet, and Firefox for Android (latest two major versions) Then first-open binding, same-device reopen, device-mismatch OTP, and private-mode fallback each function as specified And no browser presents a blocker preventing binding or OTP And in OEM SMS in-app webviews where storage is blocked, OTP fallback is shown and completes successfully
Time-Boxed Expiration and Auto-Extend Logic
"As a guest, I want the link to stay valid until my pickup time so that I can pay when it's convenient, but not after pickup to prevent misuse."
Description

Apply a server-enforced TTL to each link based on the pickup window plus a configurable grace period. Display a visible countdown on the payment page, and auto-extend when prep or pickup time changes via order updates. Immediately invalidate the link upon staff-marked pickup or payment completion to prevent reuse. Include background jobs to sweep and expire stale links, robust timezone handling, and a staff-initiated extension flow with audit logging.

Acceptance Criteria
Server-Enforced TTL and Visible Countdown
Given an order with pickup_end at T_end and a grace period of G minutes configured for the store When the customer opens the payment link before T_end + G Then the server responds with HTTP 200 and includes an expires_at timestamp (UTC) equal to T_end + G computed from the store timezone And the payment page displays a countdown reflecting (expires_at − now) that updates at least every second And when the countdown reaches zero, the UI disables payment actions and shows "Link expired" And any subsequent GET/POST to the link returns HTTP 410 Gone with error_code=LINK_EXPIRED
Auto-Extend on Order Time Change
Given a valid link with current expires_at = E_old And the order’s pickup_end is updated to T_end_new in the system When T_end_new + G > E_old Then expires_at is updated to E_new = T_end_new + G without issuing a new link And the customer’s open page reflects the new countdown within 5 seconds without manual refresh And an audit entry of type=auto_extend records {order_id, token_id, old_expires_at, new_expires_at, source=order_update, correlation_id}
No Auto-Shrink on Earlier Pickup Updates
Given a valid link with current expires_at = E_old And the order’s pickup_end is updated earlier to T_end_new where T_end_new + G < E_old When the update is saved Then expires_at remains E_old and is not reduced automatically And an audit entry of type=no_change records {order_id, token_id, attempted_expires_at=T_end_new+G}
Immediate Invalidation on Pickup or Payment Completion
Given a valid link open on a customer device When staff marks the order as Picked Up in CurbPing or the payment provider returns a successful charge Then the link token is immediately invalidated server-side And any further requests using the token return HTTP 410 Gone with error_code=LINK_CONSUMED And any open payment page receives a real-time event and disables actions within 2 seconds And an audit entry records {event_type: picked_up|payment_complete, order_id, token_id, actor_id, timestamp}
Background Sweep for Stale Links
Given links whose expires_at < now and status=active When the background sweep job runs every 60 seconds Then all such links are marked expired and are no longer redeemable And the sweep is idempotent and safe to run concurrently And metrics are emitted {expired_count, duration_ms} and logs include link_ids processed And the job completes within 30 seconds while processing at least 10,000 links
Timezone and DST-Safe Expiry Computation
Given a store timezone TZ and an order with pickup_end expressed in TZ on a day with a DST transition When expires_at is computed Then expires_at equals pickup_end + grace period as a ZonedDateTime in TZ converted to UTC And in DST gap/overlap cases, the computation resolves using standard time library rules without errors And countdowns for customers in any timezone reflect the correct remaining time
Staff-Initiated Extension with Audit Logging
Given a staff user with permission to extend links and a valid link with current expires_at = E_old When the staff member selects Extend Link and enters a new expiry E_new > E_old with a required reason note Then the server updates expires_at to E_new And the customer’s open page reflects the new countdown within 5 seconds And an immutable audit record is created with {user_id, order_id, token_id, old_expires_at, new_expires_at, reason, timestamp} And the order timeline shows the extension to all staff users
OTP Fallback and Step-Up Verification
"As a guest, I want to verify with a texted code if I switch devices so that I can still pay without calling the store."
Description

When a device mismatch, high-risk signal, or excessive attempts are detected, require a one-time passcode delivered via SMS to the phone number on the order. Support resend with cooldown, rate limits, temporary lockouts, and localized templates. Provide an accessible, low-friction OTP UI optimized for SMS deep links. Persist OTP attempts and verification results with timestamps; ensure codes are short-lived and unusable after success or expiry.

Acceptance Criteria
Step-Up Triggering on Risk Signals
Given a guest opens a payment link bound to a prior device And the system detects at least one: device fingerprint mismatch with the bound device, fraud risk score ≥ 0.80, or ≥ 3 failed payment/link access attempts in 10 minutes When the guest attempts to proceed to payment Then the system requires OTP verification before proceeding And the OTP UI displays with masked destination phone number from the order And the trigger reason and UTC timestamp are recorded in the order audit trail And if none of these risk conditions are present, the OTP step is not shown
OTP SMS Delivery and Localization
Given the system requires OTP verification When an OTP is issued Then a 6-digit numeric code is sent via SMS to the phone number on the order And the SMS template is localized using the order locale (or inferred from country code), falling back to en-US And the message is compatible with iOS/Android OTP AutoFill heuristics and includes the merchant name and domain And if the SMS gateway indicates non-delivery within 15 seconds, the UI shows a delivery error and allows a resend after cooldown
Resend Cooldown and Rate Limiting
Given the OTP screen is displayed When the guest taps Resend Then the resend action is disabled for 30 seconds before another resend is allowed And no more than 5 OTP sends are allowed per order within 15 minutes And each resend invalidates all previously issued, unexpired codes And rate-limit events are recorded with UTC timestamps in the audit log
OTP Lifespan and Single-Use Validation
Given an OTP code has been issued When the guest enters a code that matches the latest issued code And the entry occurs within 5 minutes of issuance Then verification succeeds and all outstanding codes for the order are invalidated And the guest is returned to the prior flow to complete payment When the entered code is expired or does not match Then verification fails with a generic error (no hint which part is wrong) And the guest may try again unless limited by attempt or lockout rules
Attempt Limits and Temporary Lockouts
Given the guest is attempting OTP verification When 5 failed verification attempts occur within 15 minutes for the order Then OTP verification is locked for that order for 15 minutes And the input and resend controls are disabled during lockout with a countdown indicating remaining time And the failure count resets after a successful verification or when the lockout period elapses
Accessible, Low-Friction OTP UI with Deep Link Optimization
Given a guest lands on the OTP-required flow (including from an SMS deep link) When the OTP page loads on mobile or desktop Then focus is set on the first OTP input with descriptive labels and instructions meeting WCAG 2.1 AA And a numeric keypad is invoked on mobile, paste is supported, and digits auto-advance across inputs And the UI supports single-field entry with auto-formatting where available And the deep link opens directly to the OTP screen with order context and supports OS-level OTP autofill And the masked destination phone number and merchant name are visible and the phone number cannot be edited
Persistence and Audit of OTP Events
Given OTP issuance, resend, verification, rate-limit, and lockout events occur When any such event happens Then the system persists order_id, phone hash, device fingerprint, IP, user agent, trigger reason, outcome, attempt count, and UTC timestamp And OTP code values are never stored in plaintext; only salted hashes of active codes are stored and deleted on success or within 10 minutes of issuance, whichever comes first And audit records are retained for 90 days and are queryable for fraud review
Fraud Screening and Risk Scoring Engine
"As an operator, I want suspicious links to be screened and step-upped automatically so that chargebacks are reduced without burdening good guests."
Description

Evaluate each link open and payment attempt with a rules-based risk score: geodistance between store and IP geolocation, device velocity and reuse, proxy/VPN detection, BIN risk, prepaid indicators, name/phone mismatches, and anomalous access times. Configure thresholds to allow, require OTP, or block. Integrate with gateway fraud tools and maintain allow/deny lists. Persist risk reasons for explainability and dispute support.

Acceptance Criteria
Risk Scoring on Link Open
Given a customer opens a Link Lock URL for a specific store When the risk engine evaluates IP geolocation, device fingerprint, phone number, and timestamp Then it returns a risk_score between 0 and 100 and a decision in {ALLOW, CHALLENGE_OTP, BLOCK} based on configured challenge_threshold and block_threshold And the risk_score, decision, and top 5 risk_reasons are persisted with a correlation_id for this link_open event And the evaluation completes within 250 ms at p95 And if any external enrichment (IP intelligence, device graph, geo lookup) fails Then the decision is CHALLENGE_OTP and risk_reason signal_unavailable is recorded
Risk Scoring on Payment Attempt with Gateway Signals
Given a customer initiates payment from a Link Lock session When pre-authorization enrichment (BIN lookup, prepaid indicator, card country) is applied Then the risk score is updated before authorization and a decision in {ALLOW, CHALLENGE_OTP, BLOCK} is produced based on configured thresholds If the decision is BLOCK before authorization Then the authorization request is not sent, the customer sees a generic failure message, and the event is persisted with reasons If the decision is CHALLENGE_OTP before authorization Then the user must complete OTP verification within the configured TTL to proceed; on success the decision becomes ALLOW for this attempt; on failure or expiry the decision becomes BLOCK After authorization When AVS/CVV/3DS results are returned by the gateway Then the risk score is re-evaluated; if the post-auth decision is BLOCK, the authorization is voided or not captured and the order is marked fraud_blocked; all reasons are persisted When the cardholder name or billing phone from the gateway mismatches the order name or pickup phone beyond configured tolerance Then risk_reason identity_mismatch is added and the score adjusted per configuration
Geodistance and Anomalous Access Time Rules
Given an IP geolocation for a link open or payment attempt When the distance between the IP location and the store location exceeds the configured geodistance_threshold Then risk_reason geo_distance_exceeds_threshold is added and the risk score increases by the configured amount When the access time falls outside the store's configured operating window or outside configured allowed hours Then risk_reason anomalous_access_time is added and the risk score increases by the configured amount When IP geolocation is unavailable or low confidence Then risk_reason geo_confidence_low is added and no geodistance rule is applied
Device Velocity and Reuse Controls
Given a device fingerprint observed across events When the device initiates more than the configured max_orders_per_T orders or uses more than the configured max_cards_per_T unique cards within period T Then risk_reason device_velocity_exceeded is added and the decision is at least CHALLENGE_OTP When the same device is used with multiple distinct phone numbers or names above the configured threshold within T Then risk_reason device_identity_mismatch is added and the decision is escalated per configuration When the device has a recent deny-listed history within the rolling window Then the decision is BLOCK When the device matches an allow list entry Then velocity/reuse penalties are suppressed for that device
Proxy/VPN and Network Risk Detection
Given an incoming IP address When the IP is identified as a known proxy/VPN, hosting provider, TOR exit node, or has an ASN risk score above the configured threshold Then risk_reason network_high_risk is added and the decision escalates per configuration When the IP is private, reserved, or otherwise non-routable Then risk_reason ip_invalid_for_geo is added and geodistance is skipped When the same high-risk IP is observed across more than the configured number of distinct devices or phone numbers within T Then the decision becomes BLOCK
Allow/Deny Lists and Overrides
Given identifiers (phone_number, device_fingerprint, card_fingerprint, IP) present on the allow list When a risk evaluation occurs Then the final decision is forced to ALLOW unless a block_override flag is present Given identifiers present on the deny list When a risk evaluation occurs Then the final decision is forced to BLOCK regardless of score When allow/deny lists are updated by an authorized admin Then the change propagates to the risk engine within 60 seconds and all changes are audit-logged with actor, timestamp, and reason
Risk Reasons Persistence and Auditability for Disputes
Given any risk evaluation Then the system persists: correlation_id, inputs considered, rule hits with weights, threshold values, risk_score, final decision, and top 10 risk_reasons And records are retained for at least 180 days and are retrievable via admin API by correlation_id, order_id, phone_number, or time range And an append-only audit log with checksums records all rule/config changes and evaluation outcomes; CSV/JSON evidence can be generated on demand for disputes And personally identifiable data is stored and displayed in compliance with configured data minimization (tokenized card data; redacted IP last octet)
Staff Console Controls and Overrides
"As a curbside lead, I want to see and manage link status in real time so that I can help guests and keep the line moving."
Description

Enhance the staff dashboard to show per-order link status (bound device, expiry timer, risk score, OTP status) and provide controls to extend expiry, resend link, force OTP, invalidate, or approve with manager override after ID check. Require role-based permissions and manager PIN for overrides. All actions must be auditable with user, timestamp, and reason. Optimize for mobile use at the curb.

Acceptance Criteria
View Link Status on Order Card
Given a logged-in staff user with Order:View permission When the user opens an order in the Staff Console on list or detail view Then the UI displays for that order: Device-Bound state (Bound/Unbound with masked device), Expiry countdown timer, Risk score (numeric 0–100 and categorical badge), and OTP status (Not Sent/Pending/Verified/Failed) And the expiry countdown updates every second until expiration And the risk score badge maps to thresholds: Low (0–39), Medium (40–69), High (70–100) And field tooltips expose plain-language explanations for each status And values reflect backend state within 5 seconds of a change And no PII beyond masked identifiers is shown
Extend Expiry with Audit Trail
Given a user with Order:ExtendExpiry permission viewing an Active or Expired link When the user selects Extend and chooses a duration (e.g., +5m, +10m, +15m) and enters a reason Then the order’s expiry is extended by the selected amount relative to current time if expired, or relative to current expiry if active And the countdown timer reflects the new expiry immediately And an audit record is created with user, timestamp, previous expiry, new expiry, and reason And the action is disabled if the order is already Completed or Invalidated
Resend Link to Customer
Given a user with Order:ResendLink permission and a valid customer phone on file When the user selects Resend Link and confirms the action Then a new payment/arrival link SMS is sent to the customer’s phone number on file And the current link state remains Active (unless previously Expired or Invalidated) And the UI shows a success/failure toast with the carrier response status And an audit record is created with user, timestamp, delivery attempt ID, and reason (if provided)
Force OTP Challenge
Given a user with Order:ForceOTP permission viewing an order with an Active link When the user selects Force OTP and confirms with a reason Then the link requires OTP verification on next use And an OTP is issued to the customer phone on file And the order’s OTP status updates to Pending and then to Verified or Failed based on entry And an audit record is created with user, timestamp, and reason
Invalidate Link Immediately
Given a user with Order:InvalidateLink permission viewing an order with an Active or Pending link When the user selects Invalidate and confirms with a reason Then the link becomes Invalidated and cannot be used to pay, signal arrival, or complete pickup And any open sessions using the link are blocked on next action with an invalid message And the order’s link status updates to Invalidated in the console And an audit record is created with user, timestamp, and reason
Approve with Manager Override (PIN)
Given a user attempts an override approval for an order with high risk or failed OTP When a Manager-role user enters a valid manager PIN and selects a reason (e.g., ID matched, guest known) Then the order is marked Approved with Override and bypasses link restrictions for pickup completion And the console records the approving manager’s user ID, timestamp, and reason And non-Manager users cannot complete this action and are shown a permission error And the override action is unavailable once the order is Completed
Mobile-Optimized Curb Workflow
Given the Staff Console is opened on a mobile device with 360x640 viewport When a user views an order card and action sheet Then all primary controls (Extend, Resend, Force OTP, Invalidate, Approve w/ PIN) are visible without horizontal scrolling And touch targets are at least 44x44 px with 8 px spacing And actions are reachable within two taps from the order card And content maintains a minimum 4.5:1 contrast ratio for text/icons
Audit Trail, Metrics, and Webhooks
"As an owner, I want detailed logs and metrics so that I can prove disputes and monitor the impact on chargebacks and wait times."
Description

Record an immutable event log for link lifecycle events: created, opened, device bound, OTP sent/verified, payment authorized/captured, expired, invalidated, overridden, and pickup confirmed. Redact sensitive data and apply retention policies. Expose real-time webhooks to POS and payment gateways to auto-expire links after pickup and sync payment status. Provide metrics dashboards for conversion, OTP rate, step-up rate, fraud blocks, and chargeback outcomes, with anomaly alerts.

Acceptance Criteria
Immutable Link Lifecycle Event Logging
Given a new Link Lock payment link is created for order_id X at time T0 When lifecycle actions occur (created, opened, device_bound, otp_sent, otp_verified, payment_authorized, payment_captured, expired, invalidated, overridden, pickup_confirmed) Then the system appends one event per action within 1 second including event_id (UUIDv4), link_id, order_id, event_type, occurred_at (UTC ISO-8601 ms), actor (system|user|staff), outcome (success|failure), and metadata keys with sensitive values redacted And existing events cannot be updated or deleted via any API; attempted updates return HTTP 409 and create a compensating event event_type=event_correction with reason_code And OTP codes, full IP addresses, raw device fingerprints, PANs, CVVs, and full cardholder names are never stored; OTP and PAN-derived fields are stored only as salted hashes; IPs are stored as /24 (IPv4) or /48 (IPv6) prefixes And retention policies apply: full events retained 365 days; PII-bearing metadata (ip_prefix, device_fingerprint_hash) retained 30 days then purged; aggregated metrics preserved And each event contains previous_event_hash and current_event_hash (SHA-256) to form a chain; a daily integrity job verifies the chain and emits an alert on any break
Webhook Delivery and Idempotency to POS and Gateway
Given merchant POS and gateway webhook endpoints are configured with shared secrets When events payment_authorized, payment_captured, pickup_confirmed, expired, or invalidated occur Then a signed HTTPS POST is delivered within 3 seconds p95 containing idempotency_key, event_id, event_type, link_id, order_id, occurred_at, and payload And the request includes an HMAC-SHA256 signature header and timestamp with a 5-minute tolerance window And deliveries are retried with exponential backoff for up to 24 hours until a 2xx is received, with a maximum of 10 attempts per endpoint per event And duplicate deliveries are safe for receivers using idempotency_key; our system guarantees at-least-once delivery And on pickup_confirmed, the platform issues an expire_link webhook to the gateway within 3 seconds and disables further customer access to the link And webhook outcomes (success/failure, response code, latency) are written to the audit log
Metrics Dashboards: Conversion, Step-Up, Fraud, Chargebacks
Given Link Lock is active and the dashboard time window and filters (store, location, brand, channel) are set When viewing metrics Then the following are displayed with freshness <= 2 minutes p95 and accurate within ±0.5% of event counts: links_created, unique_opens, device_bound_rate, OTP_sent_rate, OTP_verify_success_rate, step_up_rate, payment_authorization_rate, payment_capture_rate, conversion_created_to_captured, fraud_block_rate, chargeback_count and chargeback_rate And users can segment by device type, pickup bay, hour of day, and UTM parameters; results update within 2 seconds p95 And users can export CSV for the current view and schedule daily email reports; exports complete within 60 seconds for up to 100k rows And drill-down from any metric lists up to the last 100 contributing link_ids with their event timelines, with fields redacted per policy
Anomaly Detection and Alerting
Given baselines are computed as trailing 7-day same-hour averages per store When for 15 consecutive minutes any of these thresholds is breached: conversion_created_to_captured drops ≥20%, OTP_verify_success_rate drops ≥15%, fraud_block_rate rises ≥10%, or webhook_delivery_success drops below 98% Then an anomaly alert is sent within 2 minutes to configured channels (email, SMS, Slack) containing metric, current value, baseline, store, timeframe, and severity And alerts have a 30-minute cooldown per metric-store pair and auto-resolve when the metric returns within 1 standard deviation of baseline for 15 minutes And all anomalies are appended to the audit log with correlation_id and acknowledgement state
Retention Policies and Right-to-Erasure
Given a data subject erasure request tied to a customer phone or link_id When the scheduled purge job runs Then personally identifiable fields (phone_hash, ip_prefix, device_fingerprint_hash, email_hash) are removed or irreversibly anonymized within 7 days while non-identifying aggregates are preserved And purge_started and purge_completed events are written with counts of records affected and a verification checksum And subsequent API/UI queries must not return purged fields; aggregate metrics remain consistent within ±0.1% over the affected window
Access Control and Tamper-Evident Audit Integrity
Given a user attempts to access audit or metrics data via API or UI When authorization is evaluated Then access requires role-based permissions with row-level scoping by merchant_id; cross-tenant access is denied with HTTP 403 and logged And all access is logged as events (read_audit, export_audit, view_metrics) with who, what, when, and source IP prefix And audit and metrics data at rest is encrypted with AES-256 and in transit with TLS 1.2+; storage is immutable (WORM) with retention enforcement And a monthly integrity report is generated and downloadable, showing hash-chain verification status, webhook delivery p95/p99, and retention compliance

RightBay AI

Auto-selects the optimal bay by blending live occupancy, predicted dwell times, quoted prep windows, walking distance from the staging door, and runner workload. Captains get fewer conflicts to juggle, guests park right the first time, and runners spend less time crossing the lot—keeping food hotter and handoffs faster.

Requirements

Live Bay Occupancy
"As a curbside captain, I want accurate live bay availability so that I can trust assignments and avoid sending two cars to the same spot."
Description

Maintains real-time state for each curbside bay (available, assigned, occupied, blocked) using order lifecycle events from the 'I'm here' link, staff handoff confirmations, and timeouts. Handles creation and expiration of holds, late arrivals, and manual clear actions without requiring sensors or beacons. Exposes a fast read API for the assignment engine and the captain console, and persists state with audit logs for troubleshooting and analytics. Ensures a single source of truth for bay availability to prevent double-booking and reduce conflicts.

Acceptance Criteria
Arrival Hold Creation via 'I'm here' Link
Given a guest opens the 'I'm here' link for an active order and an available bay is selected When the arrival event is processed Then the system creates a hold for that order and sets the bay state to assigned within 250 ms And the hold has a TTL of 6 minutes (configurable per store) And duplicate arrival events within 60 seconds are idempotent and do not create additional holds And no two active holds may reference the same bay at the same time
Hold Expiration and Late Arrival Reassignment
Given a bay in assigned state with an active hold When the hold TTL elapses without the order transitioning to occupied Then the hold is released and the bay state becomes available within 250 ms And the order reverts to awaiting-bay status and is eligible for reassignment And an audit log records hold expiration with reason timeout And if the guest taps 'I'm here' after expiration, a new hold is created subject to current availability
Occupied on Arrival Confirmation and Free on Handoff
Given a bay in assigned state for an order When the guest confirms Parked via the browser link or a captain marks Arrived in the console Then the bay state transitions to occupied within 250 ms and is excluded from new assignments And the associated order records the bay and occupied timestamp When a captain records Handoff complete for the order Then the bay state transitions to available within 250 ms and the order's curbside session is closed And an audit log records both transitions with actor (guest/captain) and timestamps
Manual Clear of Bay State
Given a bay in assigned or occupied state When a captain clicks Clear bay and confirms Then the bay transitions to available within 250 ms and any hold or occupancy link to the order is removed And the affected order is updated to awaiting-bay (if not handed off) without creating a new hold And an audit log entry captures the manual clear with actor, previous state, order ID, and reason
Block and Unblock Bay Availability
Given any bay state When a captain sets the bay to blocked with an optional reason Then the bay state becomes blocked within 250 ms and is excluded from assignment and display as available And any active hold is canceled and linked orders are updated to awaiting-bay When the captain unblocks the bay Then the bay returns to available within 250 ms And all actions are audit logged with actor and reason
Fast Read Bay State API for Assignment and Console
Given the assignment engine or captain console requests current bay states via GET /bays/state When up to 200 bays exist and up to 50 writes per second occur Then the API returns a consistent snapshot with p95 latency <= 100 ms and p99 <= 200 ms And each bay entry includes id, state (available|assigned|occupied|blocked), orderId (nullable), holdExpiresAt (nullable), blockedReason (nullable), updatedAt And responses include an ETag and lastModified for cache validation And data freshness is within 200 ms of the latest committed write
Persistence and Audit Logging of Bay State Changes
Given any bay state change (including holds, expirations, manual clears, block/unblock, occupy, free) When the change is committed Then an immutable audit record is persisted with timestamp (ms), sequence id, bay id, previous state, new state, order id (nullable), actor (system|guest|captain), reason, and correlation id And audit records are queryable by time range, bay id, and order id within 500 ms for 1-day ranges And audit data is retained for at least 90 days and survives service restarts And the current bay states are restored on restart within 2 seconds using the persisted store
Dwell Time Prediction
"As an operator, I want the system to anticipate how long a car will occupy a bay so that we can plan assignments that keep flow smooth and waits short."
Description

Predicts expected curbside dwell time per order using historical order-handoff durations, time-of-day/day-of-week patterns, customer history, quoted prep windows, and contextual signals (e.g., weather, order size) to forecast bay turnover. Outputs a probabilistic estimate with confidence that the scoring engine uses to minimize idle bay time and reduce reassignment. Trains incrementally, supports cold-start defaults, and degrades gracefully if data is sparse.

Acceptance Criteria
Probabilistic Output Contract
Given a valid order payload (orderId, storeId, quotedPrepWindow, timestamp, customerId, orderSize, weather), When the dwell prediction API is called, Then the response is 200 and includes orderId, distribution {p10, p50, p90} in minutes, confidence ∈ [0,1], modelVersion, generatedAt, expiresAt ≥ now+5m, and units="minutes". Given a successful prediction response, When validating the distribution, Then p10 < p50 < p90 and all values ≥ 0. Given confidence < 0.5, When inspecting the payload, Then lowConfidence=true is present. Given non-critical features are missing, When the API is called, Then featuresMissing contains those signals and the call succeeds with 200. Given an invalid payload (missing orderId or storeId), When the API is called, Then the response is 400 with a machine-readable error code and message.
Calibration and Accuracy Thresholds
Given a rolling 7-day evaluation set with ≥ 300 completed orders per store, When computing prediction coverage, Then actual dwell time falls within [p10, p90] for ≥ 80% of orders. Given the same dataset, When computing MAE of p50 vs actual, Then overall MAE ≤ 4 minutes and peak-hour (11:30–13:30, 17:00–19:00) MAE ≤ 6 minutes. Given predictions grouped by confidence ≥ 0.7, When computing MAE, Then MAE ≤ 3.5 minutes for that group. Given predictions grouped by confidence < 0.4, When computing coverage, Then coverage ≥ 60%.
Incremental Training and Model Versioning
Given ≥ 50 new labeled handoffs or 15 minutes elapsed (whichever comes first), When the trainer job runs, Then a candidate model is trained and evaluated within 10 minutes. Given the candidate passes validation thresholds from Calibration and Accuracy Thresholds, When promoting, Then modelVersion increments and is deployed with zero-downtime within 5 minutes and rollback enabled. Given live traffic during deployment, When serving predictions, Then error rate < 0.1% and p95 latency ≤ 250 ms. Given a 10% canary for 30 minutes, When detecting metric regression > 5% vs baseline, Then automatic rollback triggers and traffic returns to the prior stable model.
Cold-Start and Sparse-Data Degradation
Given a new store with < 100 historical orders, When requesting predictions, Then the system uses global priors + time-of-day/day-of-week + quotedPrepWindow and returns p50 within quotedPrepWindow ± 5 minutes with confidence ≤ 0.4. Given missing customer history and/or weather signals, When requesting predictions, Then the API responds 200 with featuresMissing populated and confidence reduced but ≥ 0.1. Given the store accumulates ≥ 100 orders spanning ≥ 14 days, When requesting predictions, Then a store-adapted model is used and median confidence increases by ≥ 0.15 vs cold-start baseline.
Context Signal Responsiveness
Given two identical synthetic orders except weather=heavy_rain vs weather=clear, When predicting, Then p50(heavy_rain) ≥ p50(clear) + 10% and |confidence_diff| ≤ 0.05. Given orderSize=large (>6 items) vs orderSize=small (≤2 items), When predicting, Then p50(large) ≥ p50(small) + 15%. Given runnerWorkload=high (dispatchQueue p95 > 3) vs runnerWorkload=low (≤ 1), When predicting, Then p50(high) ≥ p50(low) + 10%. Given quotedPrepWindow increases by 10 minutes, When predicting, Then p50 increases by ≥ 5 minutes.
Latency, Throughput, and Uptime SLOs
Given steady load of 50 req/s per region and bursts to 200 req/s for 2 minutes, When serving predictions, Then p95 latency ≤ 250 ms, p99 ≤ 500 ms, and success rate ≥ 99.9%. Given a dependency slowdown (feature store or weather API p95 > 1 s), When serving predictions, Then circuit breakers use cached/default features and p95 latency ≤ 300 ms with success rate ≥ 99%. Given a single-region outage, When traffic fails over, Then failover completes within 2 minutes and error rate remains < 1% during the 15-minute incident window.
Integration with RightBay Scoring Engine
Given an occupied bay with predicted release in T minutes and an incoming order with predicted dwell D, When the scoring engine runs, Then the assignment uses dwell predictions and simulated reassignments per 100 orders decrease by ≥ 20% vs baseline over a 14-day A/B dataset. Given a malformed prediction (NaN or negative values), When the scoring engine receives it, Then it substitutes store-median dwell, sets predictionError=true, and continues without failure. Given predictions with confidence < 0.3, When scoring, Then the dwell-time signal weight is reduced by ≥ 50% and the reason is logged.
Assignment Scoring Engine
"As a curbside captain, I want the system to recommend the best bay automatically so that guests park right the first time and handoffs are faster."
Description

Computes an optimal bay recommendation by weighting live occupancy, predicted dwell times, quoted prep windows, walking distance from the staging door, and runner workload. Supports configurable weights per location, hard constraints (e.g., ADA bay reservations, blocked bays), and tie-breakers. Returns a top-ranked bay and confidence, with deterministic behavior under identical inputs. Exposes synchronous API used when a guest taps "I'm here" or when staff pre-assigns during peak, and logs decisions for auditability.

Acceptance Criteria
Optimal Bay Recommendation on Guest Arrival ("I'm here")
Given location L has 12 bays with live occupancy O, dwell predictions D, quoted prep window P for order O123, walking distances W, runner workloads R, and active weight profile W1 When the recommend-bay API is called synchronously with order_id=O123 and event_type="im_here" Then the response is HTTP 200 containing bay_id (string), confidence (0.00–1.00), decision_id, and weight_profile_id And the selected bay is neither occupied nor blocked at decision time And the selected bay’s weighted score under W1 is the maximum among all eligible bays within ±0.001 of the reference calculation And P95 latency <= 300 ms and P99 <= 500 ms over 1,000 requests in a controlled test
Hard Constraints: ADA and Blocked Bays Observed
Rule 1: ADA-reserved bays are included in the candidate set only when request.ada_required = true; otherwise they are excluded. Rule 2: Bays with status in {blocked, closed, under_maintenance} are excluded from the candidate set. Rule 3: If the candidate set is empty, the engine returns HTTP 409 with error_code = "NO_ELIGIBLE_BAY" and no recommendation payload. Rule 4: When ada_required = true and at least one ADA bay is free, the recommended bay is an ADA bay.
Configurable Weights Per Location and Versioning
Given two distinct weight profiles W1 and W2 configured for location L When W2 is activated for L Then the next recommendation for L uses W2 and the response includes weight_profile_id = W2.id And for a fixed test fixture where W1 emphasizes walking distance and W2 emphasizes runner workload, the recommended bay differs between W1 and W2 as predicted by the weighted calculation And weight profile changes take effect within 60 seconds of activation and are applied deterministically to all subsequent requests.
Deterministic Behavior Under Identical Inputs
Given identical request payloads, the same weight_profile_id, the same live-signal snapshot timestamp, and the same candidate set When the API is called three times within 5 seconds Then all responses return the same bay_id, confidence, and decision_id prefix (trace stable) And shuffling the order of candidates in the snapshot does not change the chosen bay And two separate service instances produce identical outputs for the same inputs.
Tie-breaker Ordering on Equal Scores
Rule 1: If two or more eligible bays have equal composite scores within ε = 0.001, select the bay with the lower walking_distance_m. Rule 2: If still tied, select the bay with the lower assigned_runner_workload. Rule 3: If still tied, select the bay with the lower numeric bay_id. Rule 4: In a seeded equal-score fixture, the returned bay matches the deterministic tie-breaker order across repeated runs.
Synchronous API Contract for Arrival and Pre-assign Use Cases
Given a synchronous endpoint that accepts: location_id, order_id, event_type ∈ {"im_here","preassign"}, optional ada_required, optional vehicle_info When a valid request is made Then a 200 response includes: bay_id, confidence, decision_id, weight_profile_id, decided_at (ISO-8601) And P95 latency <= 300 ms for event_type = "im_here" and <= 400 ms for event_type = "preassign" at 20 RPS per location in staging And error responses include an HTTP status and machine-readable error_code with message.
Decision Logging and Audit Retrieval
Given any recommendation decision When the decision is committed Then an immutable audit record is stored with: decision_id, location_id, order_id, redacted request payload, candidate list with per-factor and composite scores, chosen bay_id, confidence, constraints applied, weight_profile_id, decided_at And the record is retrievable by decision_id and order_id within 5 seconds via an API And audit records are retained for at least 30 days and exportable as JSON.
Bay Map & Door Distance Config
"As a store manager, I want to configure our bays and door location so that the system can choose nearer bays and reduce runner travel time."
Description

Provides a simple setup flow to define bay identifiers, positions, and the primary staging door, then computes and stores walking distances and zone groupings. Supports drag-and-drop layout, numeric distance entry, ADA bay flags, and temporary bay closures. Makes distances available to the scoring engine and runner balancing, ensuring recommendations minimize cross-lot travel without requiring external mapping tools.

Acceptance Criteria
Initial Bay and Door Setup Wizard
Given a new location with no bays configured When the operator launches the setup wizard Then they can add bays with unique identifiers Given the operator is placing bays When they position a bay via drag-and-drop Then the position is captured for that bay Given bays have been added When the operator selects a primary staging door Then the selection is required to proceed and is persisted Given bays and a primary door are defined When the operator saves the setup Then walking distances for each bay to the primary door are computed and stored Given the setup is saved When the operator reloads the configuration Then all bays, positions, the primary door, and computed distances are present and accurate
Drag-and-Drop Layout Updates Distances
Given an existing map with bays and a primary door When a bay marker is dragged to a new position Then the bay's position change is saved Given a bay's position changed When distances are recomputed Then the bay's walking distance to the primary door is recalculated and stored within 1 second Given multiple bays are moved When the operator saves the map Then all affected distances are recalculated and no stale distance values remain
Manual Distance Entry and Overrides
Given a bay has an auto-computed distance When the operator enters a numeric distance value Then the value is validated as a non-negative number and saved as a manual override Given a bay has a manual override distance When the bay's position changes Then the manual distance remains in effect until the override is explicitly cleared Given a bay has a manual override distance When the operator clears the override Then the system reverts to using the auto-computed distance
ADA Bay Flagging
Given a bay exists When the operator enables the ADA flag for that bay Then the ADA status is saved and indicated in the layout Given a bay is flagged ADA When configuration is published to the scoring engine Then the bay's ADA flag is included in the payload Given a bay is flagged ADA When the layout is edited or distances recomputed Then the ADA flag remains unchanged unless explicitly toggled by the operator
Temporary Bay Closures
Given a bay exists When the operator marks it as temporarily closed with an optional expiration time Then the bay is excluded from assignment and visually indicated as closed Given a bay is temporarily closed with an expiration time When the expiration is reached Then the bay automatically reopens and becomes available for assignment Given a bay is temporarily closed When configuration is published to the scoring engine Then the closure state is included and the bay is not offered for selection
Zone Grouping Computation and Use
Given bays and a primary door are defined When zone grouping runs Then each bay is assigned to a zone and the zone identifier is stored Given bay positions change When zone grouping runs Then zone memberships are recalculated and updated consistently Given zone identifiers exist When runner balancing requests configuration Then each bay's zone is included for workload distribution
Data Availability to RightBay Engines
Given computed distances, zone memberships, ADA flags, and closure states exist When the scoring or runner balancing engine requests configuration Then the response includes current values for each bay without requiring any external mapping services Given a configuration change is saved When the scoring or runner balancing engine requests configuration Then the updated values are available within 5 seconds Given an engine requests data for a non-existent bay ID When the system processes the request Then it returns a clear error and does not include partial or stale data
Runner Workload Balancer
"As a runner, I want assignments that consider my current load so that I’m not crisscrossing the lot and can keep food hotter."
Description

Monitors active handoffs per runner, their assigned zones, and recent travel to estimate near-term capacity. Feeds a workload score to the assignment engine to avoid overloading a single runner and to sequence pickups to even out effort. Supports single-runner and multi-runner operations, shift changes, and manual overrides, and updates in real time as handoffs are marked complete.

Acceptance Criteria
Real-time Workload Score Refresh on Handoff Completion
Given a runner with 2 active handoffs and a current workload score S When one of the handoffs is marked complete Then the Runner Workload Balancer recalculates the runner’s workload score and publishes it to the assignment engine within 1 second And the new score is strictly lower than S And the published payload includes runnerId, timestamp, activeHandoffs, recentTravelMeters(5m), assignedZones, and workloadScore
Even Distribution of Assignments Across Multiple Runners
Given 3 active runners in the same zone and 5 ready-for-handoff orders over 3 minutes When the balancer sequences assignments using workload scores Then at any time the difference in concurrent active handoffs between any two runners does not exceed 1 unless a runner is manually set to unavailable And no runner exceeds the configured maxConcurrentHandoffs
Zone-Adherent Assignment with Controlled Cross-Zone Escalation
Given a pickup in Zone A and at least one Zone A runner has workloadScore < zoneSaturationThreshold When the balancer ranks eligible runners Then all Zone A runners are ranked ahead of non-Zone A runners And no cross-zone recommendation is emitted Given a pickup in Zone A and all Zone A runners have workloadScore >= zoneSaturationThreshold When the balancer ranks eligible runners Then the top-ranked runner may be from another zone if their workloadScore is lowest after applying walkingDistance penalties And the decision is logged with reason "Zone saturated" and includes candidate runnerIds with workloadScores
Manual Override Updates Workload and Freezes Assignment
Given a captain manually assigns Runner X to Order Y When the override is saved Then the balancer increases Runner X’s workload score to reflect the new active handoff and publishes the update within 500 ms And Order Y is marked overrideLocked and excluded from automatic rebalancing And an audit log entry records userId, timestamp, prior top 3 candidates, and Runner X’s post-override workloadScore
Shift Change Reallocation and Off-Shift Shielding
Given Runner X clocks out with N active handoffs When the clock-out is confirmed Then the balancer marks Runner X unavailable within 1 second and publishes a score update including unavailable=true And X’s active handoffs are re-queued and reassigned to the lowest workload runners in the same zones within 2 seconds And no new assignments are issued to Runner X after the clock-out timestamp
Single-Runner Operation Behavior
Given exactly one runner is active When new orders become ready or arrivals are detected Then the balancer computes and publishes workload score updates for the runner on each state change within 1 second And no balancing deferral occurs due to lack of peers And the runner never exceeds maxConcurrentHandoffs
Performance and Telemetry Under Peak Load
Given up to 8 runners and 30 simultaneously active handoffs When state-change events (create/update/complete, overrides, shift changes) occur at up to 10 events per second Then p95 time from event receipt to workload score publish is <= 200 ms and p99 <= 350 ms And 100% of publishes include runnerId, eventType, timestamp(ISO8601 ms), activeHandoffs, recentTravelMeters(5m), assignedZones, workloadScore, unavailable flag And dropped or duplicate publishes are <= 0.1% over any 15-minute interval
Auto-Reassignment & Conflict Handling
"As a curbside captain, I want the system to resolve bay conflicts automatically so that I spend less time juggling spots and guests aren’t confused."
Description

Detects conflicts such as simultaneous arrivals, no-shows, early/late arrivals, or bay overruns, and automatically reassigns to the next best bay when needed. Notifies captains and guests of changes with clear, single-source instructions, and respects constraints like ADA and blocked bays. Includes guardrails to avoid thrashing (e.g., limit reassignments per order, minimum hold durations) and provides one-tap staff overrides.

Acceptance Criteria
Simultaneous Arrivals Conflict Resolution
Given two or more orders are assigned or tentatively reserved for the same bay within overlapping arrival windows When their “I’m here” confirmations are received within a 60-second window Then the system resolves the conflict deterministically using this priority order: (1) ADA compliance, (2) order ready state reached, (3) earlier confirmation timestamp, (4) closer to quoted prep window, (5) shorter predicted dwell, (6) lower runner workload impact, (7) lowest order ID as tiebreaker And the winning order retains the contested bay and each losing order is auto-reassigned to the next-best compliant bay within 3 seconds of conflict detection And the captain dashboard and guest web link reflect the final bay assignment within 1 second of reassignment And an update message is dispatched to affected guests within 2 seconds of reassignment And no user sees two different active bay instructions for the same order for longer than 1 second
No-Show Reclaim and Reassignment
Given an order is marked Ready and assigned to a bay When no “I’m here” confirmation or location heartbeat is received within 7 minutes after the ready time Then the order is flagged as No-Show and the bay is freed within 1 second And the captain is notified immediately in the dashboard and the guest is sent a no-show SMS/notification dispatched within 2 seconds And if there is a waiting/arriving compatible order (ETA within 5 minutes), the freed bay is assigned to the highest-priority compatible order within 3 seconds And an audit entry records the no-show reclaim with timestamps and affected order IDs
Early Arrival Deferral and Thrash Guardrails
Given a guest taps “I’m here” more than 5 minutes before the quoted ready time When all non-blocked bays are occupied or reserved for orders ready within the next 5 minutes Then the system does not assign a bay and places the order in a virtual wait state, showing a wait message with current estimate on the guest web view within 1 second And once within 5 minutes of ready or a non-conflicting bay frees, the system assigns the best-fit compliant bay within 3 seconds and dispatches an update within 2 seconds And for any order with a bay assigned, a minimum hold of 3 minutes is enforced before auto-reassignment unless the bay becomes blocked or an ADA conflict is detected And auto-reassignments are limited to a maximum of 2 per order, with at least 90 seconds between auto-reassignments
Bay Overrun Reallocation
Given an active order’s dwell exceeds its predicted dwell time plus a 2-minute buffer and a conflicting arrival is within 3 minutes of ETA When the overrun condition is detected Then the incoming arrival is reassigned to the next-best compliant bay within 3 seconds if available And if no alternative bay exists, the captain is prompted within 1 second to choose: extend current occupant or manual reassign; the guest receives a wait/redirect update dispatched within 2 seconds And the current occupant is not auto-reassigned unless a safety-critical or ADA constraint requires it And all changes are reflected in the captain dashboard and guest web link within 1 second
ADA and Blocked Bay Compliance
Given ADA-designated bays and orders flagged as ADA-required When auto-assignment or auto-reassignment is performed Then ADA-required orders are only assigned to ADA bays unless a manual override is explicitly applied by staff And non-ADA orders are never auto-assigned to ADA bays while any compliant non-ADA bay is available And bays marked Blocked (temporarily or permanently) are never eligible for auto-assignment/reassignment And if no compliant bay exists, the order remains queued/unassigned; the captain is alerted and the guest is informed to wait, with no non-compliant auto-assignment performed
Single-Source Instructions Consistency
Given any reassignment occurs for an order When the new bay is finalized Then the guest web link and the captain dashboard each display only the latest bay assignment within 1 second And the next outbound message to the guest includes an explicit update (e.g., “Updated”) and the new bay, instructing them to ignore prior messages And prior bay indicators for that order are visually marked as superseded in the captain UI and are not actionable And the public API/webhook emits exactly one current assignment record per order within 2 seconds (no duplicates)
One-Tap Staff Override Lock and Audit
Given a captain initiates a manual override on an order When the captain taps a suggested bay tile Then the override completes in a single tap with no modal confirmation by default and the assignment is locked for 5 minutes And during the lock window, auto-reassignment is suppressed except for safety-critical conditions (bay becomes blocked, ADA conflict detected, emergency closure) And the captain dashboard and guest web link update within 1 second; an update message is dispatched within 2 seconds And an audit log is written within 1 second capturing user, timestamp, previous bay, new bay, and reason “manual override”
Real-time SMS Bay Instructions
"As a guest, I want a simple text telling me exactly where to park so that I can pull in without calling the store or circling the lot."
Description

Sends immediate, clear bay instructions to guests via SMS when a bay is assigned or reassigned, including bay number, landmarks, and any special notes. Updates the existing 'I’m here' web view to mirror the current assignment and supports multilingual templates. Ensures delivery with retry and link tracking, and suppresses churn by avoiding unnecessary message changes when the car is already parked.

Acceptance Criteria
Initial Bay Assignment SMS Delivery
Given a guest triggers the "I'm here" link and RightBay AI assigns a bay When the assignment is confirmed in the system of record Then send an SMS within 5 seconds containing Bay {number} first, landmarks, special notes (if any), and a unique tracking link And record a message-sent event tied to the arrival session and assignment ID And do not send a duplicate SMS for the same assignment within 60 seconds
Reassignment Before Parking
Given the guest is not marked Parked and a bay change is required When RightBay AI reassigns the guest to a different bay Then send an "Updated bay" SMS within 5 seconds with new bay number, landmarks, notes, and the tracking link And cancel any pending retries for the superseded assignment And ensure no more than 3 outbound bay-instruction SMS are sent per guest per arrival session
Parked State Churn Suppression
Given the guest is marked Parked by explicit tap or geofenced presence in the assigned bay When a reassignment is generated by the system Then do not send any bay-change SMS And log a suppression event with reason "Parked" linked to the arrival session And limit suppression logging to at most one event per minute per session
Web View Mirrors Current Assignment
Given the guest has the "I'm here" web view open When an assignment or reassignment occurs Then update the web view within 3 seconds to show the current bay number, landmarks, and notes And ensure the bay shown matches the latest system assignment And render the web view in the guest's language preference
Multilingual SMS Template Rendering
Given a guest language preference exists (e.g., en, es) When composing the SMS Then select the template for that language and correctly render {bay_number}, {landmark}, {notes}, and {link} And if a template for the language is unavailable, fallback to English And ensure the final SMS content length is 320 characters or fewer
Delivery, Retry, and Link Tracking
Given an SMS is sent to the guest When the carrier delivery receipt indicates failure or no receipt is received within 60 seconds Then retry up to 2 times with delays of 30 seconds then 90 seconds And do not retry if the guest has clicked the tracking link or is marked Parked And record delivery timestamp(s), retry count, and first link-click timestamp And ensure the tracking link resolves the session and loads the web view with p95 < 2 seconds

CurbNav SMS

Sends clear, turn‑by‑turn lot directions via text with simple landmarks (entrance, row letters, signage) and a lightweight web map pin—no app needed. If conditions change, guests get an instant reroute to the new bay so misparks drop and phone‑tag disappears.

Requirements

Smart SMS Directions Composer
"As an arriving guest, I want clear text directions and a map pin for my assigned bay so that I can reach the correct spot without calling the restaurant."
Description

Generate concise, human-friendly turn-by-turn directions using the store’s configured lot schema (entrances, row letters, bays, signage) and attach a lightweight web map pin link—no app required. Automatically compress copy to 1–2 SMS segments, prioritize simple landmarks over street names, and tailor wording for driving vs. walking approaches. Fallback gracefully to text-only directions if link preview is blocked or the device is offline. Integrates with CurbPing’s order/bay assignment so messages are triggered at “Order Ready” or on guest “I’m here” detection. Ensures per-store branding, short-linking, and template versioning for easy copy updates. Outcome: guests reach the correct bay on the first try, cutting phone-tag and misparks.

Acceptance Criteria
Driving Directions: 1–2 SMS Segments With Landmarks
Given a store lot schema with entrances, row letters, bay numbers, and signage is configured And an order has an assigned bay And the transport mode is driving When the composer generates directions Then the message references only configured landmarks (entrances, row letters, signage) and avoids external street names unless no landmark exists And the final step explicitly states the target bay and row (e.g., "Park in Bay 7, Row C") And a lightweight map pin short-link is appended to the SMS And the link uses the store’s branded short-domain And the total encoded SMS length (body + link) fits within 1–2 segments for the detected encoding (<=306 chars GSM-7 or <=140 chars UCS-2) And the message contains no more than 2 sentences before the final bay callout
Walking Directions Tailoring
Given a store lot schema with pedestrian landmarks (entrances/doors, sidewalk paths, signage) is configured And an order has an assigned pickup location And the transport mode is walking When the composer generates directions Then the copy uses pedestrian verbs and nouns (e.g., "Walk", "entrance", "door") and contains no driving/parking terms And directions reference only pedestrian-accessible landmarks from the schema And the final step directs the guest to the pickup area without parking instructions And a map link is appended with a pedestrian mode parameter And the total encoded SMS length fits within 1–2 segments for the detected encoding
Graceful Fallback: Text-Only Directions When Link Unavailable
Given the system detects offline state for the guest device or a link/preview is blocked per capability check or prior delivery feedback When the composer prepares the message Then the SMS is sent without any hyperlink And the text includes complete, stepwise directions and the explicit bay/row callout And the message still fits within 1–2 segments for the detected encoding And no follow-up link-only SMS is sent for the same event And an event is logged with reason=fallback and type=text_only
Instant Reroute on Bay Change
Given an order has already received directions to Bay A And the assigned bay changes to Bay B after initial send When the change is saved Then a reroute SMS is sent within 10 seconds And the message clearly states the update (e.g., "Update: now Bay B, Row C") and includes an updated map link And only one reroute SMS is sent per bay change event (no duplicates) And the previous short-link redirects to the new bay or is expired within 60 seconds And all sends are logged with correlation to the original message
Auto-Trigger at Order Ready and "I'm Here"
Given an order transitions to Order Ready status When the order becomes Ready Then directions are sent once per order unless already sent in the last 15 minutes And if the guest taps "I'm here" before directions were sent, the message is sent immediately at tap time And if directions were already sent at Order Ready, the "I'm here" event does not trigger a duplicate send And all triggers record source=order_ready or source=im_here with timestamps
Per-Store Branding, Short-Linking, and Template Versioning
Given a store has configured sender ID, branded short-link domain, and an active template version When the composer generates an SMS Then the SMS is sent from the configured sender ID And all links use the store’s branded short-domain and resolve to the correct map pin And the message text matches the selected template version content rules And the send is logged with store_id, template_version_id, and link_id metadata And switching the active template version reflects in the next message without code deployment
Directions Use Lot Schema and Resolve to Correct Bay
Given the lot schema contains entrances, row letters, bay numbers, and signage labels And an assigned bay exists in the schema When the composer generates directions Then every referenced landmark exists in the schema with exact label matching And the final instruction resolves to the assigned bay id and row letter And a validation harness confirms the path terminates at that bay And if required schema elements are missing, the composer sends a generic fallback (e.g., "Follow pickup signage to Bay X") and logs a schema_missing error
Dynamic Reroute Messaging
"As a guest already en route, I want to be instantly notified if my pickup bay changes so that I don’t park in the wrong place."
Description

When bay assignments or lot conditions change, instantly notify en-route guests with an updated SMS and refreshed web pin link that highlights only the delta (e.g., “Bay B3 → C2”). Invalidate prior links, show an in-page reroute banner, and throttle notifications to avoid spam while guaranteeing delivery. Provide one-tap staff controls in the CurbPing console to trigger moves and an API/webhook to automate from kitchen/queue systems. Include idempotency, deduplication, and audit logs. Outcome: misparks and manual calls drop as guests are redirected in real time.

Acceptance Criteria
Delta Reroute SMS With Refreshed Pin and Link Invalidation
Given a guest is assigned bay B3 and is en route When the assignment changes to C2 via console or API Then the system sends a single SMS within 5 seconds that includes the delta "B3 → C2" and a refreshed lightweight map link And the refreshed link opens to a page centered on bay C2 with the bay label highlighted and an in‑page "Rerouted to C2" banner displayed for at least 10 seconds And any previously issued arrival/map links for that order are invalidated: opening them redirects to the current link with the reroute banner, and direct calls to the old link ID return HTTP 410 or a 302 to the new link with reason=rerouted And if the guest already has the arrival page open, it updates to C2 and shows the reroute banner within 2 seconds without a manual refresh
Notification Throttling With Delivery Guarantee
Given multiple bay changes occur for the same order within a 60-second window When reroute processing runs Then the system sends at most one SMS in that window containing only the latest bay and updates the web pin accordingly And a per-guest throttle of one SMS per 30 seconds is enforced, coalescing intermediate changes And delivery is confirmed via carrier DLR; if no DLR within 60 seconds, retry up to 3 times with exponential backoff and failover to an alternate provider on the final retry And if all retries fail, the console displays "Delivery failed" with a timestamp and offers a one-tap "Call guest" fallback action
Idempotent and Deduplicated Reroute Requests
Given a reroute request includes an Idempotency-Key and targets the same order and bay as a prior completed request within 24 hours When the request is processed Then no additional SMS is sent and the same response body and audit record ID are returned And if duplicate reroute requests without an Idempotency-Key arrive within 10 seconds resulting in the same final bay, only one message is sent and one audit record is written And if two concurrent reroutes target different bays, last-write-wins is applied using server timestamps, and only the latest state is messaged while earlier queued messages are canceled
One‑Tap Reroute From Staff Console
Given a staff user with reroute permission views an active order in the CurbPing console When they tap "Move to [Bay]" and select C2 Then the reroute is issued immediately without additional confirmation modals and inline status updates from Sent → Delivered (or Failed) are shown And the action button is temporarily disabled to prevent double-send until a status is received or 5 seconds elapse And if another user changed the bay within the last 2 seconds, a non-blocking toast indicates the update and the UI reflects the latest state And the action and status updates are keyboard accessible and announced via screen reader (ARIA live region)
Reroute Automation via API and Status Webhooks
Given an authorized integrator When they POST to /v1/reroutes with orderId, targetBay, and Idempotency-Key Then the API validates input, authenticates via API key or OAuth2 client credentials, and returns 202 Accepted with rerouteId And the guest receives the same delta SMS and refreshed link behavior as a console-triggered reroute And webhook events reroute.sent, reroute.delivered, reroute.failed, and reroute.throttled are sent within 5 seconds of state changes, signed with HMAC-SHA256, and retried up to 9 times with exponential backoff on non-2xx responses And webhook payloads include rerouteId, orderId, previousBay, newBay, source (console|api|automation), timestamps, and messageIds
End‑to‑End Audit Logging for Reroutes
Given any reroute occurs When the action completes Then an immutable audit record is stored containing actor (user or system), timestamp (UTC), previousBay, newBay, messageId(s), DLR status, idempotencyKey, throttle/dedupe decisions, and source IP/user agent when applicable And audit logs are queryable by date range, orderId, phone, and bay, returning results within 2 seconds (p95) for up to 10,000 records And audit logs are retained for at least 365 days and exportable to CSV; exports include a file hash to verify integrity And viewing or exporting logs requires appropriate role permissions and read access is itself logged with timestamp and actor
Lot Layout & Landmark Configurator
"As an operator, I want to configure my lot’s entrances, rows, and bays so that directions sent to guests match my real-world layout."
Description

Provide an operator-facing setup tool to model the parking lot: define entrances, aisles, row letters, bay numbers, curbside signs, and choke points; upload a static lot image or draw overlays; and establish default paths from each entrance to each bay. Include validation (e.g., unreachable bays), preview test-sends, and versioned drafts with publish/rollback. Support multi-location templates and per-store overrides. Data serves both the SMS copy generator and web pin renderer. Outcome: accurate, recognizable directions tailored to each unique lot.

Acceptance Criteria
Lot Elements Definition and Validation
Given I am configuring a new lot, When I add at least one entrance, one aisle, one row letter, and one bay number, Then the Save action becomes enabled. Given I define rows (letters) and bay ranges (e.g., A1–A20), When I save, Then row letters are unique per lot and bay IDs are unique across the lot (no duplicates). Given I input bay ranges within a row, When I save, Then overlapping bay numbers within the same row are rejected with a field-level error, while gaps are allowed. Given I add curbside sign IDs and choke points, When I save, Then each element requires a non-empty label and canonical ID; missing/invalid fields are flagged inline and summarized. Given validation fails, When I click Save, Then zero changes are persisted and all offending fields display messages specifying the rule violated. Given a lot up to 500 bays and 6 entrances, When I successfully save, Then the operation completes within 2 seconds at p95.
Image Upload and Overlay Editor
Given I upload a lot image (PNG/JPG/SVG) up to 10 MB and at least 1000×1000 px, When upload completes, Then the image is stored, correctly oriented, and previewed without distortion. Given the overlay editor is open, When I place, move, rotate, and label overlays for entrances, aisles, rows, bays, signs, and choke points, Then objects support pixel snapping, undo/redo, and z-ordering. Given I save overlays, When I reload the editor, Then all overlay geometries and labels reappear in the same positions within ±1 px and font size within ±1 pt. Given I use the keyboard, When I nudge selected overlays with arrow keys, Then movement occurs in 1–5 px increments and is reflected in saved coordinates. Given I use latest Chrome, Safari, and Edge on desktop, When I interact with the editor, Then all functions work without console errors.
Default Path Routing from Entrances to Bays
Given a lot graph with entrances, aisles, and choke points defined, When I generate default paths, Then each bay has a path from each entrance or is flagged as unreachable. Given one or more bays are unreachable, When validation runs, Then the UI lists the exact bay IDs and the blocking cause (e.g., missing aisle connection) with a link to edit. Given one-way aisles or closed choke points are marked, When paths are generated, Then routes respect restrictions and avoid prohibited segments. Given a lot up to 500 bays and 6 entrances, When I regenerate paths after edits, Then recomputation completes within 2 seconds at p95. Given a bay has a valid path, When I preview it, Then step instructions include row letters and landmark references in sequence from entrance to bay.
Preview Test-Send: SMS and Web Map Pin
Given I select an entrance and bay and enter a verified test phone number, When I click Send Test, Then an SMS is delivered containing lot name, entry landmark, ordered steps with row letters/signage, assigned bay, and a web map link. Given the SMS link is opened on a 4G connection, When the page loads, Then the web map renders within 2 seconds at p95 with a pin on the selected bay and the path from the chosen entrance. Given I change the assigned bay before arrival, When I trigger a reroute test-send, Then the recipient receives an updated SMS indicating the new bay and link; the previous link displays an "Updated bay" banner and points to the new bay. Given the SMS provider returns a transient failure, When sending occurs, Then the system retries up to 3 times with exponential backoff and logs the attempt outcomes.
Publish, Versioning, and Rollback
Given a Draft configuration exists, When I click Publish, Then validation must pass with zero critical errors (e.g., no unreachable bays) and the Draft becomes the Published version with version ID, author, and timestamp. Given a Published version exists, When I create or edit a Draft, Then live SMS and map rendering continue to use the Published version until a new Publish occurs. Given a version history is available, When I select Rollback to version N, Then version N becomes Published and a new version entry records the rollback action without data loss. Given two users edit concurrently, When the second user attempts to publish a stale Draft, Then the system prevents overwrite and prompts to merge or create a new Draft, preserving both sets of changes.
Multi-Location Templates and Per-Store Overrides
Given I create a Lot Template, When I assign it to multiple stores, Then each store receives an initial Draft derived from the template with a reference to inheritance. Given a store needs a local change, When I override specific fields (e.g., row labels, choke points) at that store, Then those fields are marked as overridden and no longer auto-update from the template, while non-overridden fields remain inherited. Given I update and publish the Template, When stores pull updates, Then non-overridden fields update in-place and overridden fields remain unchanged; a diff view is available before applying updates. Given I remove an override at a store, When I save, Then the store re-inherits that field from the template on next sync.
Data Contract for SMS Generator and Web Pin Renderer
Given a Published lot configuration for a store, When the SMS generator requests data, Then the API returns JSON including entrances, aisles, rows, bays, signs, choke points, and precomputed default paths, with a schemaVersion field and response time <500 ms at p95. Given a valid link token is used by the web pin renderer, When it fetches the same configuration, Then it can render a bay pin and draw the default path from the specified entrance without additional server calls. Given the schemaVersion increments, When older clients request data, Then backward-compatible fields are preserved or a 409 is returned with migration guidance; no breaking change is served under the same version number. Given required data is missing or malformed, When generators attempt to build directions, Then the operation fails gracefully with an actionable error code and trace logged for diagnosis.
Lightweight Web Pin Page
"As a guest, I want a simple web page with a pin for my bay that loads fast and updates if my bay changes so that I can navigate easily without an app."
Description

Deliver a fast-loading, no-login web page that shows the assigned bay pin, a simple arrow path, and step-by-step text synced with the SMS copy. Optimize for sub-200KB payloads, quick TTFB, and offline resilience with cached assets. Auto-center using device location (with permission), adapt to low light/dark mode, and meet WCAG 2.1 AA for contrast and screen readers. Surface live reroute updates in-page and provide a ‘Report can’t find bay’ affordance to alert staff. Outcome: frictionless, app-free navigation that works on any modern mobile browser.

Acceptance Criteria
App-free page shows bay pin, arrow path, and SMS-synced steps
Given a guest opens the unique CurbNav SMS link on a supported mobile browser (iOS Safari 15+, Android Chrome/Firefox/Edge 95+) When the page loads Then no login or app-install prompt is shown And the assigned bay pin is visible without scrolling on devices 360x640 to 428x926 And a simple arrow path from lot entrance to the assigned bay is rendered And step-by-step directions text is displayed and exactly matches the SMS copy version for the order (step count and wording) And the page is fully usable in both portrait and landscape orientations
Performance budget and load time compliance
Given first visit on a cold cache over simulated 4G (Good 4G: 1.6 Mbps/300 ms RTT) When requesting the page via the SMS link Then total transfer size (HTML+CSS+JS+images+fonts) <= 200 KB And TTFB at CDN edge <= 200 ms (p95) And First Contentful Paint <= 1.5 s (p75) And Time to Interactive <= 2.5 s (p75) Given a repeat visit (warm cache) within 24 hours Then additional network transfer <= 60 KB And FCP <= 0.8 s (p75)
Offline resilience with cached assets
Given the page has been loaded at least once with network connectivity And the device subsequently loses network access When the guest opens or returns to the page within 15 minutes Then the map shell, last known bay pin, and directions steps render from cache within 1.0 s And an "Offline – directions may be stale" banner is displayed And user actions are usable; network actions are queued And queued actions auto-send within 5 s of connectivity restoring with visible confirmation And if a reroute occurred while offline, the updated bay and steps appear within 2 s of reconnect
Geolocation auto-center with graceful fallback
Given the browser prompts for location permission When permission is granted Then the map auto-centers to the device location within 2 s And the indicated position has <= 25 m accuracy when available; if accuracy > 50 m, a non-blocking hint suggests zooming/recenter And a Recenter control is visible and operable When permission is denied or the geolocation request times out after 8 s Then the map defaults to a full-lot view And a non-blocking hint explains how to enable location, while navigation remains usable
Accessibility and dark mode compliance
Given system dark mode is enabled When the page loads Then a dark theme is applied and all text/essential graphics meet WCAG 2.1 AA contrast (>= 4.5:1 for body text, >= 3:1 for large text/icons) And tap targets are >= 44x44 px with >= 8 px spacing Given a screen reader (iOS VoiceOver or Android TalkBack) When navigating the page Then bay label, step text, map landmark, reroute banner, and report button are announced with meaningful names/roles/states in logical focus order And visible focus indicators are present for all interactive elements And automated contrast/a11y checks plus manual spot checks report no WCAG 2.1 AA failures
In-page live reroute updates
Given the guest has the page open And the assigned bay changes server-side When a reroute event is received by the client Then the bay pin, arrow path, and step text update in-page within 2 s without a full page reload And a banner/toast announces the new bay (e.g., "Bay B4") and is announced by screen readers And the previous path is cleared to avoid conflicting directions And an analytics event is recorded for the reroute
'Report can’t find bay' alerts staff
Given the 'Report can’t find bay' control is visible When the guest taps it Then a minimal confirmation appears and a single additional tap sends the alert And staff console receives the alert within 5 s including order ID, guest phone, last known GPS (if available), and current bay assignment And the guest sees an acknowledgment within 1 s and a fallback call option And the action is rate-limited to 1 submission per 30 s per order And if offline, the report is queued and auto-sent on reconnect with on-screen status updated
SMS Delivery & Compliance Layer
"As CurbPing, I want reliable, compliant SMS delivery so that messages reach guests consistently and we meet carrier and legal requirements."
Description

Integrate with approved SMS providers (e.g., A2P 10DLC) to ensure reliable delivery, link shortener with branded domain, and correct GSM/Unicode handling to keep messages within segment limits. Manage STOP/HELP/START flows, quiet hours, opt-in records, regional formatting, retries on transient failures, and per-carrier rate limits. Provide delivery receipts, bounce codes, and alerting for degradation. Outcome: compliant, dependable messaging that preserves brand trust and minimizes costs.

Acceptance Criteria
A2P 10DLC Compliance Enforcement
Given a US destination number and an active tenant When an SMS is queued Then the message is sent via a registered A2P 10DLC campaign and approved sender ID Given the tenant lacks an approved campaign or sender When attempting to send Then the send is blocked, a descriptive error is returned to the caller, and an audit event is recorded Given a campaign or sender status changes to suspended When processing sends Then all sends on that sender halt within 60 seconds and stakeholders are alerted Given a non-US destination When sending Then a region-appropriate, compliant route is selected and logged
STOP/HELP/START Compliance & Suppression
Given a handset replies STOP to a brand's sender ID/campaign When the message is received Then a single confirmation is sent within 5 seconds and future messages from that sender to that number are suppressed Given a suppressed number When any application attempts to send Then the request is rejected with a 4xx code and the attempt is audit-logged Given the handset sends START after a prior STOP When received Then consent is restored for that sender, a confirmation is sent, and the audit trail is updated Given the handset sends HELP When received Then a compliant reply including brand name, support contact, and STOP/HELP/START instructions is sent Given STOP/HELP/START processing When quiet hours are active Then compliance replies are allowed to send immediately
GSM/Unicode Handling and Segment Optimization
Given a message containing only GSM-7 characters When segmenting Then GSM-7 encoding is used and the segment count is calculated accurately Given a message containing any non–GSM-7 character When segmenting Then UCS-2 encoding is used and the segment count is calculated accurately Given a message exceeds the configured max segments (e.g., 3) When preparing to send Then it is truncated at a word boundary with an ellipsis while preserving the full shortened URL Given emojis or diacritics are present When delivered Then no mojibake occurs and the delivered content hash matches the sent payload Given long URLs are present When shortened Then total segment count decreases or stays the same compared to the original
Branded Link Shortener with Domain Safety
Given any HTTP/HTTPS URL in an outbound message When sending Then it is replaced with a short link on the tenant's branded domain (e.g., txt.curbping.com) unless already branded Given a generated short link When clicked Then it resolves over HTTPS to the intended destination and enforces HSTS Given the shortener service is unavailable or returns errors When shortening Then the message falls back to the original long URL, the send proceeds, and an alert is emitted Given normal operations When shortening links Then added latency is ≤ 200 ms at P95 per message Given tenant policies for tracking and retention When storing short link metadata Then TTL and click tracking settings are applied and no PII is stored in cleartext
Delivery Receipts, Bounce Codes, and Retry Logic
Given a message sent to a DLR-capable carrier When a delivery receipt is available Then it is recorded within 60 seconds for at least 95% of such messages Given a transient provider error (timeouts, 5xx, retryable carrier codes) When sending Then the system retries with exponential backoff up to 3 attempts and preserves per-recipient ordering Given a non-retryable error (invalid number, unknown subscriber, spam classification) When sending Then no retries occur and a normalized bounce code and reason are stored Given final delivery status is determined When viewing logs Then the normalized status (Delivered, Failed, Undeliverable, Unknown) and raw provider codes are visible to operators
Per-Carrier Rate Limiting and Provider Failover with Alerting
Given carrier TPS limits per sender When bursts of messages are queued Then messages are throttled to remain within limits and queued with FIFO ordering per recipient Given provider responses indicate throttling (e.g., 429) or near-breach When observed Then dynamic throttles adjust within 30 seconds to eliminate subsequent 429s Given sustained provider failures > 5% over 2 minutes or median latency > 2× baseline When detected Then traffic fails over to a secondary provider within 60 seconds and an on-call alert is triggered Given a failover event occurs When completed Then stakeholders receive a status notification and dashboards are annotated with the incident timeline
Regional Formatting, Timezone, Opt-In Records, and Quiet Hours
Given an input phone number When normalizing for send Then it is validated and stored in E.164 format with the correct country code Given tenant and recipient locale settings When sending templated compliance messages Then the correct localized template is used Given tenant-defined quiet hours by timezone When a non-transactional message is queued during quiet hours Then it is deferred until quiet hours end and the deferral reason is logged Given a transactional message tied to an active curbside session When outside quiet hours Then it is sent immediately and the transactional override is recorded Given a new opt-in is captured (web form, SMS keyword, POS) When recording consent Then a retrievable record is stored with phone, timestamp, source, method, IP/user, and proof text for at least 24 months
Telemetry & Mispark Analytics
"As an operator, I want to see how CurbNav SMS affects misparks and wait time so that I can optimize my lot and prove ROI."
Description

Instrument SMS clicks, web pin opens, time-to-park, reroute events, mispark reports, and staff-initiated moves. Provide dashboards by store and time range with KPIs (mispark rate, average time-to-park, reroute success, phone-tag reduction) and exportable CSV. Respect privacy by avoiding PII in URLs, aggregating metrics, and honoring data retention policies. Outcome: operators and CurbPing can quantify impact (e.g., 42% wait reduction) and iteratively improve lot setups.

Acceptance Criteria
Track SMS Link Clicks and Web Pin Opens
Given a CurbNav SMS is delivered for an order When the customer taps the link Then record an event sms_link_click with order_id, store_id, timestamp, and user_agent and exclude any PII. Given multiple taps occur within 5 minutes for the same order When computing unique_clicks Then count exactly one unique click. Given the web map page loads successfully When the page renders Then record an event map_open with order_id, store_id, timestamp, and user_agent and exclude any PII. Given the event payloads are inspected When validating URLs Then confirm no phone numbers, customer names, or address strings are present.
Measure Time-to-Park per Order
Given an order receives the CurbNav SMS When the customer first opens the map link Then start a nav_session and stamp nav_session_start_at. Given the customer taps I'm parked or staff marks Arrived in bay X When either event occurs Then compute time_to_park_seconds from nav_session_start_at and store it with order_id and store_id. Given no arrival event occurs within 30 minutes of nav_session_start_at When computing time_to_park_seconds Then set value to null and flag timed_out true. Given an order generates multiple arrival events When persisting metrics Then keep the earliest valid arrival event for time_to_park_seconds.
Reroute Events and Success Attribution
Given staff reassigns a bay for an active order When the system sends a reroute SMS Then record reroute_sent with from_bay, to_bay, order_id, store_id, and timestamp. Given the customer opens the reroute link When the page renders Then record reroute_open with order_id and timestamp. Given a reroute is sent When the customer arrives in the new bay within 15 minutes Then mark reroute_success true else false. Given multiple reroutes for the same order When computing reroute_success_rate Then evaluate the last reroute only.
Mispark Reports and Staff-Initiated Bay Moves
Given staff flags an order as parked in the wrong bay When they submit the correction Then record mispark_reported with reported_bay, actual_bay, order_id, store_id, and timestamp. Given staff moves an order to a new bay using the tool When the move is confirmed Then record staff_move with from_bay, to_bay, order_id, and timestamp. Given totals are computed for a period When calculating mispark_rate Then use misparked_arrivals divided by total_arrivals and express as a percentage with two decimals. Given a mispark flag is undone within 5 minutes When recomputing metrics Then remove the mispark from counts and recalculate mispark_rate.
Dashboard KPIs by Store and Time Range
Given a user selects one or more stores and a time range When the dashboard loads Then display KPIs: mispark rate, average time-to-park, reroute success rate, and phone-tag reduction with definitions. Given default time ranges are used When the user opens the dashboard Then allow quick filters: Last 7 days, Last 30 days, Last 90 days, and Custom range. Given the selected filters change When recomputing metrics Then update all KPIs and charts within 2 seconds for up to 100k orders. Given phone-tag events are defined as call_link_click or staff_call_placed When computing phone-tag reduction Then show percent change versus the preceding matching time window.
CSV Export of Aggregated Metrics
Given a user applies store and date filters When requesting CSV export Then generate a file with one row per store per day containing: date, store_id, orders, unique_clicks, map_opens, avg_time_to_park_seconds, misparks, mispark_rate, reroutes_sent, reroute_success_rate, phone_tag_events, phone_tag_rate. Given the CSV is generated When inspecting its contents Then confirm it contains no PII such as phone numbers, customer names, exact timestamps, or freeform notes. Given large selections up to 365 days and 200 stores When exporting Then complete the download within 10 seconds and keep file size under 50 MB.
Privacy Controls and Data Retention
Given tracking links are generated When inspecting URLs Then ensure they contain only an opaque token with no embedded PII and that tokens expire after 24 hours. Given a store disables telemetry in settings When the flag is saved Then stop logging new telemetry events immediately for that store. Given data retention is set to 90 days for event-level telemetry When the cutoff is reached Then automatically delete events older than 90 days while preserving aggregated daily metrics. Given an access request is made for raw telemetry When honoring privacy policies Then deny raw event exports and allow only aggregated CSV downloads.
Multilingual Directions & Accessibility
"As a guest who prefers Spanish, I want directions in my language so that I can follow them confidently."
Description

Enable content in multiple languages (English and Spanish at launch) across SMS and the web pin page, with locale selection via order metadata or user preference. Maintain a glossary of lot terms to ensure consistent translations and avoid characters that inflate SMS segments. Ensure screen-reader-friendly structure and concise, plain-language steps for cognitive accessibility. Outcome: clearer directions for diverse guests, reducing errors and support calls.

Acceptance Criteria
Locale resolution and persistence
Given an order has order_locale='es' and no user-set preference When the initial arrival SMS is sent and the web pin page loads Then both SMS and web content are in Spanish and the html lang='es' attribute is set And glossary-driven terms are used on both channels Given the user switches locale via SMS keyword 'EN' or the web toggle When the next system message is sent or the page updates Then all content switches to English immediately and persists for the remainder of the order session And user preference overrides order metadata; unsupported/missing locales fall back to English with no placeholder tokens
SMS localization and segment efficiency
Given a directions or reroute SMS is composed in the resolved locale When the message is validated before send Then the charset validator confirms GSM-7-only characters (diacritics transliterated) And the computed segment count is <= 2 (<= 306 GSM-7 chars including URL) And the final message contains an HTTPS short link <= 22 characters And the gateway delivery receipts report a matching segment count; otherwise the send is flagged
Web pin page localization and WCAG conformance
Given a user opens the web pin page in Spanish on a mobile device When the page loads Then all visible text, titles, ARIA labels, and announcements are Spanish with html lang='es' And the document has one H1, logical H2/H3 hierarchy, and a working skip-to-main-content link And every interactive element is keyboard operable with visible focus and logical tab order And color contrast >= 4.5:1; content reflows at 400% zoom without loss; touch targets >= 44x44 px And an aria-live region announces reroutes in the selected language without stealing focus And automated audits (axe-core/Lighthouse) show zero critical WCAG 2.2 AA violations and a score >= 90
Glossary-driven terminology consistency
Rule: A centralized glossary supplies translations for 'entrance', 'exit', 'row', 'bay', 'signage', 'level', 'left', 'right' Rule: All SMS/web templates reference glossary keys; no hardcoded occurrences of these terms exist Rule: CI linter fails on detection of hardcoded glossary terms or missing glossary keys Rule: A single pinned glossary version is used by both SMS and web for a release Rule: Sampling 5 representative templates shows identical term usage across SMS and web outputs
Plain-language, stepwise directions (cognitive accessibility)
Rule: Directions are numbered (1., 2., 3.) in both SMS and web Rule: Max 5 steps per message; each step <= 18 words Rule: English text has Flesch-Kincaid grade <= 6; Spanish text has Fernández-Huerta >= 80 Rule: No jargon or unexplained abbreviations; any abbreviations used appear in the glossary Rule: The last line includes a localized HELP instruction and a MAP link
Localized instant reroute timing and announcements
Given a guest’s assigned bay changes while the user has Spanish selected When the change event occurs Then a reroute SMS in Spanish is sent within 5 seconds of the event And the web page updates within 2 seconds, announces the change via aria-live in Spanish, and updates the step list And reroute notifications are debounced to at most one SMS every 30 seconds per order And the reroute message complies with glossary, charset, and segment limits
Fallbacks, missing translations, and keyword switching
Given a translation key is missing in the selected locale When content is rendered Then the system falls back to English with no raw keys/placeholders and emits a WARN log with template and key Given a user sends 'ES' or 'EN' (any case) via SMS When the keyword is received Then the locale switches accordingly and a confirmation SMS in the new locale is returned And if the SMS gateway encodes as UCS-2, a condensed variant is used to keep length <= 2 segments (<= 134 UCS-2 chars); otherwise the send is aborted

BaySwap Live

Detects last‑second bay conflicts or blockages and offers an automatic reassignment to the nearest viable bay. Captains can one‑tap lock or override; guests get a friendly “New Bay” update. Avoids double‑parks, tight backups, and wasted minutes circling.

Requirements

Live Bay State Engine
"As a curbside captain, I want a live view of which bays are open, occupied, or blocked so that I can trust BaySwap decisions and reduce double-parks."
Description

Implement a real-time bay state model that tracks each bay as open, assigned, occupied, blocked, or reserved using existing CurbPing signals (guest “I’m here” pings, assignment records, captain-set block flags, order readiness). The engine normalizes inputs from SMS/browser events and captain actions into a single source of truth, emits state-change events, and exposes a low-latency subscription for UI and BaySwap logic. It requires configurable bay metadata (IDs, positions, attributes), supports concurrency-safe updates, and enforces deterministic state transitions with timeouts (e.g., auto-release after dwell). This foundation ensures accurate availability, reduces double-parks, and powers conflict detection without extra hardware.

Acceptance Criteria
Deterministic Bay State Transitions
Given a bay is OPEN, When an assignment record is created for that bay, Then the bay state becomes ASSIGNED and the state record includes assignmentId and assignedAt timestamp. Given a bay is ASSIGNED, When a valid “I’m here” ping is received for that assignment, Then the bay state becomes OCCUPIED and occupiedAt is set. Given a bay is OCCUPIED, When the dwell timer expires or a departure is recorded, Then the bay state becomes OPEN and the previous assignment link is cleared. Given a bay is in any state except OCCUPIED, When a captain sets a block flag, Then the bay state becomes BLOCKED with blockReason and blockedAt set. Given a bay is BLOCKED, When a captain clears the block, Then the bay state becomes OPEN. Given a bay is OPEN, When a reservation is created, Then the bay state becomes RESERVED with reservationId and expiresAt set. Given an illegal transition is attempted (e.g., ASSIGNED -> RESERVED), When processed, Then the engine rejects the transition with no state change and records a reason code.
Concurrency-Safe Update Handling
Given two conflicting updates target the same bay within 50 ms, When processed, Then updates are serialized per bay and only the update with the newest version succeeds; the other is rejected with a concurrency error without changing state. Given the same event is retried with the same eventId, When processed, Then the engine applies it exactly once and returns idempotent success. Given out-of-order events with older versions arrive, When processed, Then they are ignored without state mutation and a stale_event metric is incremented. Given high load of 200 concurrent bays and 1000 events per minute, When processed, Then no deadlocks occur and the engine maintains per-bay consistency.
Input Normalization from Signals and Actions
Given a guest clicks the SMS “I’m here” link, When the browser event is received, Then the engine emits a normalized event type guest_arrival with assignmentId, guestId, geoHint, and receivedAt. Given a captain taps Block Bay in the staff UI, When the action is received, Then the engine emits a block_set normalized event with bayId, actorId, and reason. Given order readiness is marked Ready, When processed, Then the engine emits an order_ready normalized event and links it to the relevant assignment. Given an unknown or malformed payload arrives, When processed, Then the engine discards it without state change, logs at warn level, and increments an invalid_payload metric.
Real-Time State-Change Events Emission
Given a bay’s state changes, When the transition commits, Then a state_change event is published within 150 ms P95 and 300 ms P99 containing bayId, previousState, newState, cause, sourceEventId, transitionAt, and version. Given multiple state changes for the same bay, When events are published, Then event ordering is preserved per bay via monotonically increasing version numbers. Given no state change occurs (e.g., duplicate arrival), When processed, Then no duplicate state_change event is emitted.
Low-Latency Subscription for UI and BaySwap
Given a client subscribes without a cursor, When the connection is established, Then it receives a full snapshot of bay states within 500 ms and subsequent state_change events as a stream. Given a client reconnects with a valid cursor, When the connection is established, Then it receives any missed events in order followed by live updates without gaps or duplicates. Given normal operations, When events occur, Then end-to-end latency from transition commit to client callback is under 300 ms P99. Given backpressure or a slow client, When the send buffer overflows, Then the server signals snapshot_required and drops queued deltas; upon client acknowledgment, a fresh snapshot is sent and streaming resumes.
Timeouts and Auto-Release Policies
Given a location dwellTimeout is configured to T minutes, When a bay is OCCUPIED and no new arrival pings are received for T minutes, Then the bay auto-transitions to OPEN and emits a state_change with cause=auto_release. Given a bay is RESERVED with TTL R minutes, When R minutes elapse without assignment, Then the bay transitions to OPEN and emits a state_change with cause=reservation_expired. Given a new arrival ping is received while OCCUPIED, When processed, Then the dwell timer is reset from the latest ping time. Given a captain override is set for a bay, When auto-release would otherwise trigger, Then the auto-release is suppressed until the override is cleared.
Configurable Bay Metadata Management
Given a new bay is created with id, position, and attributes, When saved, Then the metadata validates uniqueness of id, required fields, and persists to the store. Given bay metadata is updated (e.g., position or attributes), When applied, Then the change takes effect without engine downtime and emits a bay_metadata_updated event. Given invalid metadata (duplicate id or missing required fields), When submitted, Then the engine rejects the change with validation errors and no side effects. Given metadata changes are deployed, When the engine reloads configuration, Then current bay states are preserved and mapped to updated metadata correctly.
Nearest Viable Bay Reassignment
"As an arriving guest, I want to be reassigned to the closest available bay when mine is taken so that I don’t have to circle or call the store."
Description

Build an automatic reassignment service that, upon detecting a conflict or blockage, ranks candidate bays by proximity, walk path, and current utilization, then reassigns the order to the nearest viable bay under defined constraints. The service must run within sub-2s end-to-end latency, respect configuration (max distance, avoid-cross-street, bay capacity), and expose modes: auto-apply, suggest-with-confirm, or manual only. It consumes events from the bay state engine, updates assignment records atomically, and publishes outcomes to guest and staff channels. Requires deterministic tie-breaking, backoff on rapid churn, and safe rollback if acceptance fails.

Acceptance Criteria
Auto-Apply Reassignment Within 2 Seconds
Given mode is Auto-Apply and a bay conflict or blockage event is received for an active order assigned to bay B1 And at least one viable candidate bay exists When the reassignment service processes the event Then it updates the order’s bay assignment atomically to the selected bay and writes a single versioned record And publishes a staff update and a guest “New Bay” notification with the new bay number within 2 seconds end-to-end And the measured p95 end-to-end latency across 1000 events at target load is <= 2.0s and p99 <= 2.5s
Candidate Ranking and Deterministic Tie-Breaking
Given a set of candidate bays with attributes (walk-path time, linear distance, current utilization) When the ranking is computed Then candidates are ordered by lowest walk-path time, then lowest linear distance, then lowest utilization %, then lowest bay number, then ascending bay UUID And the selected bay equals the first element of this order for identical inputs across runs (deterministic) And bays marked blocked or at capacity are excluded from the ranking
Constraint Compliance: Distance, Cross-Street, Capacity
Given configuration values: max_distance_meters = D, avoid_cross_street = true|false, per-bay capacity = C When evaluating candidates for an order location and restaurant perimeter Then no candidate beyond D meters walk-path distance is considered And if avoid_cross_street = true, candidates requiring crossing the configured street boundary are excluded And candidates at or above capacity C are excluded And if zero candidates remain, no reassignment occurs and a “No Viable Bay” event is published to staff within 1 second
Suggest-With-Confirm Flow and Captain Override
Given mode is Suggest-With-Confirm and a conflict is detected When a viable candidate bay is identified Then the captain UI displays a one-tap prompt with the suggested bay and reason within 1 second And no guest notification is sent until the captain confirms And on confirm, the assignment updates atomically and both staff and guest are notified within 2 seconds end-to-end And on override, the captain’s chosen bay is applied using the same atomic update and notification rules And if no action occurs within T seconds (configurable), the suggestion expires and backoff is applied without changing the guest assignment
Manual-Only Mode Behavior
Given mode is Manual-Only and a conflict is detected When viable candidates exist Then the system logs a suggestion to the staff activity feed with ordered candidates and reason codes And no automatic reassignment occurs and no guest notification is sent And if a captain manually selects a bay, the assignment updates atomically and notifies staff and guest within 2 seconds
Backoff on Rapid Churn and Anti-Thrashing
Given an order experiences multiple conflict events within a rolling window W seconds When the service evaluates reassignment Then an exponential backoff is applied with a minimum dwell time D between reassignments And the same order is not reassigned more than N times within window W And the service emits a reason code “backoff_active” in staff logs when backoff suppresses a reassignment
Safe Rollback and Idempotent Event Processing
Given a reassignment is in-flight or has been applied When acceptance fails due to newly blocked target bay, captain rejection, duplicate event, or publish failure Then the service performs a single atomic rollback to the prior stable assignment and records the failure reason And repeated events with the same correlation ID are processed idempotently with no duplicate assignment updates or notifications And all notifications are exactly-once per successful reassignment, with compensating update/cancel messages sent only on rollback
Captain Lock & Override Controls
"As a captain, I want to lock or override bay assignments with one tap so that I can handle edge cases and construction or deliveries without chaos."
Description

Provide one-tap controls in the ops console to lock a bay (prevent reassignment), mark a bay blocked/unblocked with optional timer, accept/override suggested swaps, and force-assign a specific bay. Include role-based permissions, inline confirmations, and immediate visual feedback. Actions must propagate to the bay state engine and reassignment service with optimistic UI updates and conflict-safe server validation. This empowers captains to handle construction, deliveries, or edge cases without breaking automation.

Acceptance Criteria
Lock Bay to Prevent Reassignment
Given I am logged in as a Captain with edit permissions in the ops console And bay B5 is unlocked and available When I tap Lock on bay B5 Then the bay tile shows Locked state and lock icon within 150 ms (optimistic) And a server confirmation arrives within 1,000 ms updating the lock with my user and timestamp And the reassignment service excludes bay B5 from all proposals and auto-assignments while locked And any incoming server event that would assign to B5 is rejected with code bay_locked and no visible flicker And the lock persists across refresh and other clients until explicitly unlocked
Block/Unblock Bay with Timer
Given bay B3 is currently unblocked When I select Block, enter reason "delivery truck", and set a timer for 10 minutes Then the bay tile shows Blocked with countdown and reason within 150 ms And the reassignment service marks B3 unavailable immediately And the block auto-expires after 10 minutes, reverting to the prior lock state And I can Unblock early with one tap and a confirmation; the bay becomes available within 1,000 ms And all block/unblock events are persisted with user, reason, and start/end timestamps
Accept Suggested Swap
Given BaySwap Live has suggested moving order ORD-123 to bay B4 When I tap Accept on the suggestion Then order ORD-123 is assigned to B4 in the bay state engine within 1,000 ms And the guest receives a New Bay SMS indicating B4 within 5,000 ms And the ops console shows a success toast and updated assignment without page reload And any prior reservation on the old bay is released and visible to all clients within 1,000 ms And an audit record is created with action accept_suggestion, user, order, from_bay, to_bay, and timestamp
Override Suggested Swap
Given BaySwap Live suggests bay B2 for order ORD-456 And bay B4 is free and not blocked or locked by another captain When I tap Override and select bay B4 Then the system validates B4 server-side and applies the reassignment within 1,000 ms And the guest receives a New Bay SMS for B4 within 5,000 ms And if validation fails (e.g., B4 just occupied), I see an error banner with the specific reason and no assignment changes occur And an audit record is stored with action override_suggestion, user, order, suggested_bay, chosen_bay, and result
Force-Assign Specific Bay with Risk Confirmation
Given order ORD-789 needs manual placement And bay B6 is currently blocked or reserved When I choose Force Assign to B6 and confirm the inline warning to bypass constraints Then the server performs conflict-safe validation and either applies the assignment and clears conflicting reservations within 1,000 ms or rejects with a specific error (e.g., bay_occupied_hard) and the UI reverts within 1,000 ms And if applied, the reassignment service honors the forced assignment and suppresses auto-moves for ORD-789 for 5 minutes And an audit entry records bypassed constraints and the confirmation text
Role-Based Permissions and Guardrails
Given roles exist: Captain (full control), Staff (view + suggest), Viewer (read-only) When a Staff or Viewer attempts any lock, block, accept, override, or force-assign action Then the control is disabled with an explanatory tooltip, or the server returns 403 and the UI shows Insufficient permissions without changing state And only Captains see the Force Assign and Lock controls And all permission denials are logged with user, action, and timestamp
Optimistic UI, Conflict-Safe Validation, and Rollback
Given two Captain clients act on the same bay or order within 2 seconds When both submit conflicting actions (e.g., one locks bay B1 while the other force-assigns to B1) Then each client shows an optimistic state immediately, but the server applies the conflict rule: lock takes precedence unless force-assign includes explicit bypass And the losing client receives a rejection within 1,000 ms; the UI reverts, shows a red toast with the server reason, and renders the latest server state And no duplicate SMS is sent to the guest; only the final accepted assignment triggers a single New Bay SMS
Guest “New Bay” Messaging
"As a guest, I want a clear, friendly “New Bay” text and on-page update so that I know exactly where to park without confusion."
Description

Deliver a clear, friendly SMS and in-page banner update when a bay changes, including the new bay number, short directions, and a tap-to-open parking view. Support brandable templates, multilingual copy, and rate-limited retries. The guest web view should live-update via server push to avoid page refresh, with high-contrast visuals and accessibility in mind. Handle edge cases (e.g., rapid successive swaps) with debounced messaging and final-state consolidation to minimize confusion.

Acceptance Criteria
SMS New Bay Notification Delivery
Given a confirmed bay reassignment for an active curbside order with a valid guest phone number When the reassignment is committed Then an SMS is sent within 5 seconds containing the new bay number, a concise direction snippet (<=120 chars), and a tap-to-open parking link And the message body uses the active brand template with all placeholders resolved And the send attempt, provider message ID, and delivery status are recorded on the order timeline Given the SMS fails to send due to provider error When the failure is detected Then the error code is logged and the retry policy is invoked
In-Page Banner Live Update
Given a guest has the curbside web view open and connected When a bay reassignment is committed Then the banner updates within 2 seconds without a page refresh to show the new bay number, short directions, and a parking link And the previous bay information is replaced, not duplicated Given the web view connection is temporarily lost When the connection is restored within 60 seconds Then the banner shows only the latest bay state with no flicker or multiple stacked notices
Tap-to-Open Parking View Link
Given a device that supports native map links When the guest taps the parking link in the SMS or banner Then the device opens a map/app showing the correct lot and bay location Given a device that does not support native map links When the guest taps the parking link Then a mobile web parking view opens with the bay highlighted Given any link tap Then the click is tracked once per session and includes the order ID and channel (SMS or Banner)
Multilingual and Brandable Templates
Given the order has a preferred language or the browser locale is available When composing the SMS and banner Then content renders in that language using the brand’s template and variables {bay_number}, {lot_name}, {directions}, {parking_link} Given the preferred language template is unavailable When composing the message Then the English default template is used and the fallback is logged Given brand colors and logo are configured When rendering the banner Then brand styles are applied while preserving required contrast levels
Rate-Limited Retries and Delivery Confirmation
Given an initial SMS send attempt returns a transient failure When retrying Then use exponential backoff at 5s, 15s, and 45s for up to 3 total attempts, stopping on first success Given multiple send attempts for the same bay change Then do not exceed 2 guest-facing SMS messages within any 10-minute window per order Given a delivery receipt is received from the SMS provider Then update the order timeline to Delivered with timestamp; if final failure occurs, set status to Failed and cease retries
Debounced Messaging for Rapid Successive Swaps
Given multiple bay reassignments occur within 15 seconds for the same order When preparing guest notifications Then debounce and send a single consolidated message showing only the latest bay Given prior bay-change messages are queued but not yet sent When a newer bay reassignment arrives within the debounce window Then cancel the queued messages and keep only the final-state message Given the guest web view is open When rapid swaps occur Then only one banner update is shown and it reflects the final bay state
Accessibility and Contrast Compliance
Given the in-page banner is rendered Then text-to-background contrast is at least 4.5:1 and interactive elements meet 3:1 against adjacent colors Given a screen reader is active When the bay changes Then the banner announces the change via an ARIA live region (polite) with a concise accessible name Given keyboard-only navigation When the banner appears Then focus moves to the banner, the parking link is reachable in tab order, and the banner can be dismissed without trapping focus Given a non-English language is used Then the page or banner region sets an appropriate lang attribute for assistive technologies
Ops Console Alerts & Updates
"As a captain, I want prominent console alerts when a bay changes so that I can coordinate runners and keep food hot."
Description

Enhance the staff dashboard with prominent, real-time alerts for conflicts and reassignments: animated bay highlights, assignment badges, and an audible cue. Provide a compact “BaySwap strip” showing the previous bay, new bay, and action buttons (Accept, Lock, Undo). Ensure updates are sub-second on stable networks, resilient to reconnects, and consistent across devices. Include a condensed list of recent swaps for situational awareness and quick recovery.

Acceptance Criteria
Real-time Conflict/Swap Alert Visibility
Given a bay conflict or auto-reassignment event is generated for an active order and the Ops Console is open on a stable network (RTT <= 100 ms, packet loss <= 1%), When the event is received, Then the affected bay tile animates and the reassigned bay tile is highlighted within p95 <= 1.0 s and p99 <= 1.5 s across 50 events. Given the same event, When it is first displayed, Then an audible cue plays exactly once per unique event within 300 ms of the visual highlight and does not repeat unless a new event occurs. Given the same event, When the alert renders, Then an assignment badge with the order identifier and status ("Conflict" or "New Bay") is visible on the relevant bay tiles for at least 5 s or until an action (Accept/Lock/Undo) is taken.
BaySwap Strip Content and Actions
Given an auto-reassignment is generated, When it arrives at the console, Then a BaySwap strip appears showing order ID, previous bay value, new bay value, and action buttons: Accept, Lock, Undo. Given the BaySwap strip is displayed, When the operator taps Accept, Then the order is confirmed to the new bay, the strip dismisses, bay badges update to the new bay, and a guest "New Bay" update is triggered within p95 <= 1.0 s of the tap. Given the BaySwap strip is displayed, When the operator taps Lock, Then the order’s bay is locked against further automatic reassignments, a lock icon appears on the bay tile, and all consoles reflect the lock within p95 <= 1.0 s. Given the BaySwap strip is displayed, When the operator taps Undo (within 2 minutes of the swap), Then the system reverts the order to the previous assignment (or cancels the swap if revert is not possible), updates badges, and sends the corresponding guest update within p95 <= 1.5 s.
Sub-second Update Performance on Stable Networks
Given a stable network (RTT <= 100 ms, packet loss <= 1%), When 100 consecutive conflict/swap events occur, Then 95% render the animated highlight, badges, and BaySwap strip within 1.0 s of server event time and 99% within 1.5 s. Given operator actions on the BaySwap strip, When Accept/Lock/Undo is clicked, Then client UI state updates within p95 <= 500 ms and server acknowledgment is reflected in the UI within p95 <= 800 ms.
Reconnect Resilience and State Recovery
Given the console disconnects for up to 5 minutes, When it reconnects, Then current bay assignments, pending BaySwap strips, lock states, and the recent swaps list are restored to the latest server state within 2.0 s. Given one or more alerts occurred during disconnect, When the console reconnects, Then missed events appear in the Recent Swaps list with accurate timestamps and an "Unseen" indicator, and no audible cue plays for historical items. Given intermittent connectivity, When multiple reconnects occur, Then no duplicate alerts or strips are displayed and a single up-to-date strip per order is shown.
Cross-Device Consistency and Synchronization
Given two or more Ops Consoles are viewing the same location, When any operator Accepts, Locks, or Undoes a BaySwap, Then all consoles reflect the new state (bay highlights, badges, lock icon, strip dismissal) within p95 <= 1.0 s. Given simultaneous actions on the same order, When conflicts occur, Then last-write-wins is applied and all consoles display a toast indicating the winning action and actor. Given an alert is acknowledged on one console, When other consoles receive the update, Then audible cues do not play and duplicate badges/strips are not shown.
Recent Swaps List for Situational Awareness
Given the Ops Console is open, When swaps occur, Then a condensed list shows at least the last 10 swaps with order ID, timestamp, previous bay, new bay, action taken (Auto/Accept/Lock/Undo), and actor (System/User). Given the Recent Swaps list, When the operator selects Undo on an item not older than 10 minutes, Then the UI and assignment state revert and synchronize across devices within p95 <= 1.0 s, and the list updates accordingly. Given a browser refresh or reconnect, When the console resumes, Then the Recent Swaps list persists and excludes items older than 24 hours.
Eligibility & Constraint Ruleset
"As an operator, I want eligibility rules for ADA and oversized vehicles so that assignments are compliant and practical."
Description

Introduce a configurable rules engine to evaluate bay eligibility per order: ADA-only bays, oversized vehicle accommodation, time-window restrictions, kitchen-ready status, no-cross-street policy, and blacklisted bays. Rules are managed per-location, versioned, and safely validated before activation. The reassignment service must consult this ruleset before proposing or applying a swap, ensuring compliant and practical assignments without manual checks.

Acceptance Criteria
Ruleset Consultation Before Propose/Apply
Given an order eligible for reassignment and an active ruleset version Rv for location L When BaySwap evaluates a swap Then eligible bays are computed strictly by Rv, excluding any bay failing any active rule Given a proposed reassignment to bay B that violates at least one rule in Rv When auto-apply is attempted Then the apply is blocked, a machine-readable reason code for the violated rule is returned, and no guest notification is sent Given any swap proposal or application event When it is logged Then the log entry includes ruleset version id, evaluated rules, and resulting eligible set size
Special Accommodation Bays (ADA & Oversize)
Given an order with ADA_required = true When computing eligible bays Then only bays with attribute ADA_only = true are included; if none exist, no swap is proposed and reason = "no_eligible_bays:ada" is logged Given an order with vehicle_size = oversize When computing eligible bays Then only bays with attribute supports_oversize = true are included; if none exist, no swap is proposed and reason = "no_eligible_bays:oversize" is logged Given an order with ADA_required = false and vehicle_size != oversize When computing eligible bays Then bays marked ADA_only = true are excluded
Time-Window Restricted Bays
Given a bay with allowed_window = [start,end) in the location’s local timezone When current local time ∉ allowed_window Then the bay is excluded from eligibility Given a bay with an allowed_window that spans midnight (e.g., 22:00–02:00) When current local time is within that wraparound interval Then the bay is included in eligibility Given allowed_window settings are updated When the next eligibility evaluation occurs Then the new window is honored without requiring a service restart
Kitchen-Ready Gate for Reassignment
Given an order with kitchen_ready = false When BaySwap evaluates reassignment Then no proposal or application is generated for that order Given an order transitions kitchen_ready from false to true while a conflict exists When BaySwap re-evaluates Then a proposal (if any eligible bay exists) is produced within 1 second and logged
No Cross-Street Policy Compliance
Given location L has no_cross_street = true and the guest is detected on street_side = S of segment G When computing eligible bays Then eligible bays are limited to those with street_side = S on segment G; if none exist, no swap is proposed and reason = "no_eligible_bays:cross_street" is logged Given location L has no_cross_street = false When computing eligible bays Then bays on other sides/segments are not excluded by cross-street policy
Blacklisted Bay Exclusion
Given bay B has blacklist_status = active (temporary or permanent) When computing eligible bays Then B is excluded from proposals and applications Given an order is currently assigned to bay B and B becomes blacklisted When BaySwap re-evaluates Then an alternative eligible bay (if available) is proposed within 2 seconds; otherwise reason = "no_eligible_bays:blacklisted" is logged
Per-Location Rules Versioning and Safe Activation
Given a draft ruleset for location L When Validate is executed Then the system reports syntax status and runs a simulation on the last 100 orders; activation is blocked if any simulated order has zero eligible bays Given a draft ruleset for location L passed validation When Activate is executed Then a new immutable version id is created and atomically set as active within 1 second; prior version is archived and remains queryable Given a ruleset version created for L When attempting to activate it for location L' (L ≠ L') Then the activation is rejected with reason = "scope_mismatch"
Fallback & Resilience Flows
"As a captain, I want a fallback flow when no bays are free or SMS fails so that I can still stage pickups without delays."
Description

Provide graceful degradation when no viable bay exists or messaging fails: suggest a staging instruction (e.g., “Pull to pickup door”) with a single canned SMS, queue the order for next-available bay, and prompt the captain for manual acknowledgment. Include retry with exponential backoff for SMS/websocket, local caching for the guest page, and clear guidance to avoid circling. All fallbacks must be logged and reversible to return to automated flow when conditions improve.

Acceptance Criteria
No Viable Bay — Staging SMS and Queue
Given a guest taps "I'm here" and the allocator reports no viable bay within 1 second When the system detects the no-bay condition Then send exactly one canned SMS with staging instruction to the guest within 2 seconds And display the same instruction and "Please do not circle" on the guest page And place the order in a next-available-bay queue ordered by arrival time And show a "Queued — No Bay" status on the captain view And prompt the captain for manual acknowledgment And suppress duplicate staging SMS until a bay is assigned or captain overrides And record the fallback start timestamp
Messaging Failure — Exponential Backoff and Deduplicated Retries
Given an outbound message (SMS or websocket) fails (non-2xx or disconnect) When a retry is scheduled Then use exponential backoff with full jitter at 1s, 2s, 4s, 8s, 16s intervals up to a max delay of 30s and max 5 attempts And stop retries immediately upon a successful delivery acknowledgment And prevent sending duplicate SMS content to the same guest within 10 minutes And buffer in-order websocket messages for up to 60 seconds and flush on reconnect And log each attempt with timestamp, channel, result, and retry count
Guest Page Local Cache — Offline Fallback Instruction
Given the guest page has been opened at least once for the order When the device goes offline or network requests fail Then load cached static assets and last-known instruction within 2 seconds And show the last instruction and bay (if any) with a "Last updated" timestamp And display clear guidance: "Stay parked. Please do not circle." And queue user actions (e.g., "I'm here") locally and auto-send on reconnect And auto-refresh the page state within 2 seconds of reconnect And log offline mode start and end events
Captain Manual Acknowledgment Prompt
Given a fallback mode is entered for an order When the captain view receives the update Then display a persistent banner or modal with the order, guest name/ID, and fallback reason And provide "Acknowledge" and "Assign Next Bay" actions And require a captain acknowledgment within the UI before dismissing the prompt And record the acknowledging user and timestamp in the audit log And highlight the order until acknowledged or a bay is assigned And if "Assign Next Bay" is used, immediately bind the order to the next free bay and exit fallback
Fallback Event Logging and Audit Trail
Given any fallback behavior is triggered, retried, acknowledged, or resolved When the event occurs Then create a structured log entry with orderId, eventType, reason, channel, attemptCount, userId (if any), timestamps, and correlationId And persist logs for at least 30 days and make them exportable by date range And ensure entries are idempotent and not duplicated on retries And mask PII in exports except last 4 of phone and guest initials And expose a per-order fallback timeline in the admin UI
Return to Automated Flow When Conditions Improve
Given an order is in fallback mode And a viable bay becomes available and messaging channel is healthy When the allocator runs the next cycle Then assign the best available bay according to standard rules And cancel any pending retry timers And send a single "New Bay" update via preferred channel(s), suppressing duplicates And clear fallback banners on captain and guest views And update the log with a "fallback_end" event and reason And if conditions degrade again, re-enter fallback without losing queue position

ParkRight Ping

Uses gentle geofence cues to nudge guests who stop near—but not in—their assigned bay. A quick SMS says “Bay 3 is one row over” with a one‑tap “I’ve moved” confirm. Reduces correction calls and speeds clean, first‑try parking.

Requirements

Near-Bay Geofence Detection
"As a curbside guest, I want the system to recognize when I’m close to my assigned bay but not quite in it so that I get timely guidance without having to call the restaurant."
Description

Implements a server-assisted, browser-location algorithm that determines when a guest has stopped within a configurable proximity to their assigned bay but is not actually within the bay’s defined geometry. Consumes location fixes initiated from the existing “I’m here” flow, applies accuracy filtering, dwell-time checks, and bay polygon containment tests, and emits a "near-but-not-in" event only when confidence and dwell thresholds are met. Designed to minimize battery and data usage by running in short windows without persistent tracking, and to respect privacy by processing only session-scoped coordinates. Integrates with CurbPing’s order session to attach bay IDs, supports per-location tuning (distance thresholds, dwell times), and exposes a simple webhook/queue event to downstream nudge and UI subsystems. Expected outcome: precise detection that triggers nudges only when helpful, reducing correction calls and misparks.

Acceptance Criteria
Detect Near-But-Not-In Within Configured Proximity and Dwell Window
Given an active order session with an assigned bay and configured thresholds {proximity_m=D, dwell_sec=T, min_accuracy_m=A, min_valid_fixes=N} When the guest starts the "I'm here" flow and at least N consecutive fixes meet accuracy <= A and speed <= 1.0 m/s And the centroid of those fixes is outside the bay polygon but within D meters of the polygon edge And the dwell time at that centroid is >= T Then emit exactly one near_but_not_in event within 1 second containing {session_id, order_id, location_id, bay_id, timestamp, distance_to_bay_m, dwell_sec, horiz_accuracy_m, confidence, thresholds_version} And record the applied threshold values in the event payload
Suppress Detection Under Poor Accuracy or Motion and Limit Acquisition Window
Given configured thresholds {min_accuracy_m=A, min_valid_fixes=N, window_sec=W} When horizontal accuracy of incoming fixes is > A or fewer than N valid fixes are observed or speed > 1.0 m/s persists Then do not emit a near_but_not_in event during that period And continue observing only within the acquisition window W started by the "I'm here" flow And after W elapses, stop requesting location updates and emit no near_but_not_in event if criteria were never satisfied
No Near-But-Not-In When Inside Bay Polygon
Given an active order session with an assigned bay polygon and configured dwell_sec=T and min_accuracy_m=A When at least N valid fixes (accuracy <= A) have a centroid inside the bay polygon with dwell >= T Then do not emit a near_but_not_in event at any time during this dwell And if an arrival event is produced by other logic, it shall not be accompanied by a near_but_not_in event
Per-Location Threshold Overrides Apply
Given a site has location-specific overrides for {proximity_m, dwell_sec, min_accuracy_m, min_valid_fixes, window_sec, cooldown_sec} When detection runs for a session at that site Then the override values are used instead of defaults, and thresholds_version reflects the site config version And when the site config is updated, new sessions started within 60 seconds use the updated thresholds
Single Debounced Event per Mispark Episode
Given a near_but_not_in event has been emitted for a session and configured cooldown_sec=CD When additional qualifying fixes arrive without the device entering the bay polygon for >= T or moving beyond the proximity radius Then do not emit additional near_but_not_in events during the cooldown window CD When the device either dwells inside the bay for >= T or exits beyond 2×proximity_m and later re-enters the near-but-not-in condition Then a new near_but_not_in event may be emitted with a new idempotency_key
Event Payload, Delivery, and Privacy Guarantees
Given a near_but_not_in event is emitted Then it is delivered to the configured webhook/queue with schema fields {event_type=near_but_not_in, session_id, order_id, location_id, bay_id, timestamp (ISO8601 UTC), distance_to_bay_m, dwell_sec, horiz_accuracy_m, confidence, thresholds_version, idempotency_key} And delivery semantics are at-least-once with retries (>=3 attempts within 90 seconds total) until acknowledged And duplicates are identifiable by idempotency_key And only session-scoped data is included; no persistent storage of raw coordinates occurs beyond transient processing required to emit the event
Contextual SMS Nudge
"As a guest who parked near my pickup spot, I want a short text that tells me exactly how to correct my parking so that I can fix it quickly without calling."
Description

Generates and sends a concise, context-aware SMS when a near-but-not-in event occurs, referencing the guest’s assigned bay and relative position (e.g., “Bay 3 is one row over”). Includes a secure one-tap link back to the session for quick confirmation. Supports templating, brand/location personalization, language selection, quiet hours, rate limiting, retry rules, and opt-out handling to comply with A2P/CTIA guidelines. Integrates with existing SMS provider(s) and fallbacks, records delivery/engagement telemetry, and exposes configuration toggles per restaurant. Expected outcome: guests receive clear, actionable nudges that lead to faster, first-try correct parking.

Acceptance Criteria
Contextual SMS Nudge Trigger and Content
Given a guest has an active curbside session with an assigned bay and is detected stopped within the lot but outside the bay boundary by 5–40 meters When the system classifies the event as near-but-not-in and the restaurant feature toggle is enabled Then the system composes an SMS using the location’s configured nudge template, includes the assigned bay number and a relative position phrase (e.g., “one row over”), and includes a single one-tap confirmation link And then the SMS is submitted to the primary provider only if the guest is not already in the assigned bay and has not canceled the session And then the message uses the configured branded/link-shortening domain and contains no sensitive PII beyond allowed tokens (e.g., first name)
Secure One‑Tap Confirmation Link
Given a nudge SMS contains a one-tap link with a signed, single-use token tied to the curbside session When the guest taps the link within 15 minutes of send Then the session is validated without additional login and marked as moved_to_assigned_bay And then all further nudge attempts for that session are suppressed And then the guest sees a confirmation screen acknowledging the move Given the link is tapped after expiry or reuse When the token is invalid or already redeemed Then the guest sees a safe, non-actionable landing explaining the status, and no new nudge is sent
Template, Personalization, and Language Selection
Given the restaurant has a nudge template set with variables {brand_name}, {location_name}, {bay_number}, {relative_direction} When a near-but-not-in nudge is generated Then the system selects the template for the restaurant/location and replaces variables with session values And then language is chosen by priority: guest/session preference > restaurant default > system default (English) And then if the fully rendered content would exceed the configured single-part limit, the system applies link shortening and trims non-critical copy while preserving bay number, relative direction, and the confirm link Given the configured template or language is missing When rendering begins Then the system falls back to the system default template and default language without error
Suppression: Quiet Hours, Rate Limiting, and De‑duplication
Given quiet hours are configured for a location When a near-but-not-in event occurs during quiet hours Then the nudge is not sent and a suppression reason is recorded And then if queue_until_end is enabled and the session is still eligible at quiet-hours end within 10 minutes, the nudge is reevaluated and sent; otherwise it is discarded Given a phone number and session receive repeated near-but-not-in events When events occur within 5 minutes of the last nudge or after 3 nudges in the session Then additional nudges are suppressed with reason rate_limited Given multiple near-but-not-in events differ by less than 10 meters or 2 minutes When evaluating sends Then events are de-duplicated so only one nudge is sent
Retry Rules and Provider Fallback
Given the primary SMS provider returns a transient failure (timeout, 5xx) When sending a nudge Then the system retries up to 2 times with exponential backoff and marks attempts idempotently to avoid duplicate end-user messages And then if the final primary attempt fails, the system sends via the configured fallback provider once Given the provider returns a permanent failure (4xx policy/invalid number) When sending a nudge Then no retries are attempted and the send is marked failed with the provider error code And then all retries stop immediately if the session ends, the guest opts out, or quiet hours begin
Opt‑Out Handling and Compliance
Given a phone number is marked opted out or replies with STOP/UNSUB or locale equivalents When a nudge would be sent Then the nudge is not sent and the attempt is recorded as suppressed_opt_out And then future nudges remain blocked until a START/UNSTOP opt-in is received Given compliance disclosures are required by configuration When rendering a nudge Then the message includes brand identification and opt-out instructions (e.g., “Reply STOP to opt out”)
Telemetry, Analytics, and Per‑Restaurant Configuration
Given a nudge is attempted When the system processes the send Then it records telemetry including session_id, phone, template_id, language, provider, message_id, send_time, delivery_status, delivery_time (if available), click_time (if clicked), and confirmation_state Given per-restaurant configuration toggles exist for enablement, quiet hours, rate limits, templates, language default, and provider order When configuration is updated Then subsequent nudge decisions use the new configuration without requiring a deploy And then audit logs capture who changed what and when for compliance
One-Tap Move Confirmation
"As a guest who just adjusted my parking, I want to confirm I’m in the right bay with one tap so that staff know exactly where I am."
Description

Provides a frictionless, single-tap confirmation flow in the SMS that re-opens the session, re-checks the guest’s location against bay geometry, and, if corrected, updates the order state to “Parked in Bay <ID>” while notifying staff. If still incorrect, offers a concise follow-up hint and an option to request staff assistance. Requires no login or app install, uses signed session tokens for security, and handles poor GPS conditions with clear prompts. Expected outcome: a closed loop that verifies correction and eliminates back-and-forth calls.

Acceptance Criteria
One-Tap Link Reopens Session Without Login
Given an active order session O and an SMS containing a one-tap "I've moved" link with a signed session token When the guest taps the link in a mobile browser Then the session for O is reopened without any login or app install And the session page becomes interactive within 2 seconds on 4G at p50 and within 5 seconds at p95 And the order O and assigned bay are correctly displayed
Location Re-Check Confirms Correct Bay and Updates Order
Given the session is reopened and the assigned bay B has defined polygon geometry When the guest’s location is acquired with horizontal accuracy <= 15 meters within 8 seconds And the location point falls within bay B’s polygon with a 5-meter buffer Then the order state updates to "Parked in Bay B" And staff are notified in the staff console within 3 seconds with a single notification event And the guest sees a confirmation message indicating successful parking
Out-of-Bay After Move Triggers Hint and Assistance Option
Given the session is reopened and the guest is within the site geofence but outside assigned bay B When the location re-check determines the nearest bay and the offset to bay B Then the guest receives a concise hint with direction and approximate distance to bay B (e.g., "Bay 3 is one row east, ~18m") And the order state does not change to Parked And a one-tap "Request Assistance" option is presented And if the guest taps "I've moved" again, the location re-check restarts
Poor GPS Accuracy Prompts Clear Guidance and Safe Handling
Given the session is reopened When geolocation is unavailable within 8 seconds or accuracy radius > 30 meters Then the guest is shown clear steps to improve location (enable location services, move to open sky) and offered "Request Assistance" And no "Parked" state change or staff notification occurs And the guest may retry location without needing a new link
Signed Session Token Security and Expiry Enforcement
Given the "I've moved" link contains a signed, time-bound session token scoped to the order When the token is expired, malformed, revoked, or signature verification fails Then the session is not reopened and no order details are revealed And the guest sees a minimal error with a one-tap "Send me a new secure link" And the token is valid for a maximum of 15 minutes and becomes unusable after a successful confirmation
Idempotent Handling of Multiple Taps and Retries
Given the guest taps the "I've moved" link multiple times or retries the flow When duplicate confirmation requests are received within a 60-second window Then only one transition to "Parked in Bay B" is executed upon first valid confirmation And subsequent duplicates return the current state without additional staff notifications or SMS And all attempts are associated to the same session for auditability
Outcome Metrics and Event Logging for Closed Loop
Given any one-tap move attempt completes When the outcome is determined (Corrected, Still Out-of-Bay, Assistance Requested, Token Invalid, GPS Poor) Then an event is logged with timestamp, order ID, bay ID (if applicable), outcome type, location accuracy, and notification delivery status And aggregated success rate and median time-to-correction are available in the admin reporting view
Parking Bay Geometry Manager
"As a restaurant manager, I want an easy way to map and label our bays so that guests receive correct, lot-specific guidance."
Description

Adds an operator tool to define, visualize, and manage parking bay polygons and row groupings on a map or lot photo. Supports mobile-friendly drawing, snap-to-grid aids, labeling (e.g., Bay 1–12), versioning with draft/publish, validation (no overlaps, minimum size), and test mode to simulate detections. Exposes a read-optimized geometry service for runtime detection, with per-location configuration and import/export. Expected outcome: accurate, maintainable bay maps that power reliable near-bay detection and precise messaging.

Acceptance Criteria
Mobile Polygon Drawing & Editing
Given a location map or lot photo is loaded on a touch device, When the operator taps "Draw Bay" and traces a polygon with at least 3 vertices, Then a bay polygon is created and vertices are draggable. Given snap-to-grid is enabled with a configured grid size, When a vertex is moved within one grid unit of a grid line, Then the vertex snaps to the nearest grid line. Given an edit has been made to any polygon, When the operator invokes Undo or Redo, Then the last geometry change is reverted or re-applied without data loss. Given a bay polygon edge is long-pressed, When the operator selects "Add Vertex", Then a midpoint vertex is inserted and can be adjusted. Given the operator selects a bay, When "Delete Bay" is confirmed, Then the polygon and its label are removed from the draft.
Geometry Validation: No Overlaps, Min Size, and Label Uniqueness
Given two bay polygons overlap by more than the configured tolerance, When Save Draft or Publish is attempted, Then the action is blocked and both bays are highlighted with an "Overlap" error listing conflicting labels/IDs. Given a bay polygon area is less than the configured minimum area, When Save Draft or Publish is attempted, Then the action is blocked with a "Bay too small" error and the bay is highlighted. Given a bay label duplicates an existing label within the same location, When Save Draft or Publish is attempted, Then the action is blocked with a "Duplicate label" error and focus moves to the field. Given validation errors exist, When the Validation panel is opened, Then errors are listed by type with counts, and selecting an item zooms to the offending geometry.
Row Grouping and Auto-Labeling
Given a row named "Row A" is created, When bays are assigned to this row via multi-select or lasso, Then each bay displays the row tag and its order index. Given auto-labeling is invoked with pattern "Bay {n}", start=1, direction=left-to-right, When applied to selected bays in a row, Then unique sequential labels are assigned according to the row order. Given a row's order is changed by drag-and-drop, When saved, Then the persisted order updates and label sequences reflect the new order without duplication. Given a bay is removed from a row, When saved, Then remaining bays re-index their order and labels remain unique.
Draft, Publish, and Version History
Given unsaved changes exist, When Save Draft is clicked, Then a new draft version is persisted with versionId, author, and timestamp visible in history. Given a draft passes all validations, When Publish is confirmed, Then it becomes the active published version with timestamp and publisher recorded, and previous published versions remain in history. Given a published version is selected in history, When Revert is confirmed, Then that version becomes the active published version without altering the current draft. Given a publish has completed, When the runtime geometry service is queried, Then it serves the latest published version within 30 seconds and never serves drafts. Given a user lacks publish permission, When they attempt to publish, Then the action is denied with an authorization error and no state change occurs.
Test Mode: Simulate Arrivals and Near-Bay Detection
Given a published bay map, When Test Mode is enabled and a simulated device point is placed inside a bay polygon, Then the system reports status "In Bay" with the correct bay label. Given a simulated device point lies within the configured near-bay distance of the assigned bay but outside its polygon, When evaluated, Then the system reports status "Near Assigned Bay" and indicates the nearest row direction. Given a simulated device point is closer to a different bay than the assigned one, When evaluated, Then the system reports status "Near Wrong Bay" and identifies the correct bay and distance. Given multiple simulated points are added, When Run Simulation is executed, Then results are logged with coordinates, computed bay/row, status, and timestamp, and can be cleared in one action.
Runtime Geometry Service: Schema, Consistency, and Performance
Given a valid locationId, When GET /geometry/{locationId} is called, Then the response is 200 with a GeoJSON FeatureCollection whose features include polygons and properties bayLabel, rowLabel, versionId, and updatedAt. Given the same published version is requested with If-None-Match matching the current ETag, When GET /geometry/{locationId} is called, Then the service returns 304 within 100 ms. Given a location with up to 100 bays, When the endpoint is requested, Then p95 response time is <= 200 ms and payload size is <= 500 KB. Given an unknown locationId, When the endpoint is requested, Then the service returns 404. Given a new version is published, When the endpoint is called after propagation, Then the ETag changes and the new geometry is returned.
Import and Export of Bay Maps
Given a valid GeoJSON FeatureCollection of bay polygons with labels and optional row tags, When Import with Dry Run is executed, Then the system reports counts for valid features, duplicates, overlaps, and too-small shapes without persisting changes. Given a valid import with zero blocking errors, When Import is executed, Then a new draft version is created containing the imported geometry and labels. Given Export is requested for a location, When executed, Then a GeoJSON file is downloaded using CRS WGS84 containing all bay polygons and properties including bayLabel, rowLabel, versionId, and updatedAt for the selected (draft or published) set. Given an import contains any duplicate labels relative to existing bays in the location, When executed, Then the import is rejected transactionally with a report of duplicates and no partial data is saved.
Staff Console Nudge Visibility
"As a curbside runner, I want to see when a guest was nudged and whether they moved so that I can avoid unnecessary calls and focus on handoffs."
Description

Enhances the staff dashboard to display near-bay events, nudge status (sent/delivered/clicked), and real-time confirmation of corrected parking. Provides clear chips/badges on orders (Near Bay, Nudged, Moved), timestamps, and controls to resend a nudge or mark as assisted. Suppresses noisy alerts via rate limiting and merges duplicate events for the same order. Expected outcome: staff gain situational awareness and can intervene only when needed, reducing phone-tag and keeping food hot.

Acceptance Criteria
Near-Bay Event Visibility
Given an active order assigned a bay and the customer's device triggers a near-bay event outside the assigned bay When the event is received by the platform Then the order card in the Staff Console displays a "Near Bay" chip within 2 seconds And the chip includes a timestamp in the store’s local time And the event is appended to the order’s activity log And the "Near Bay" state is visible in list and detail views consistently
Nudge Status Lifecycle
Given an order with a Near Bay event When an automated nudge SMS is initiated Then the order displays a "Nudged" chip with substatus "sent" and a timestamp When the SMS provider posts a delivery receipt Then the substatus updates to "delivered" within 2 seconds of receipt and logs the timestamp When the guest taps the nudge link Then the substatus updates to "clicked" within 2 seconds and logs the timestamp
Corrected Parking Confirmation
Given a guest taps the "I've moved" link or the system detects the device in the assigned bay When the platform verifies bay-corrected status Then the order shows a "Moved" chip with a timestamp And any "Near Bay" chip is cleared or downgraded to inactive And the order is removed from Near-Bay attention filters And no further nudges are sent for this order
Resend Nudge with Rate Limiting
Given an order has an active Near Bay state and a prior nudge was sent When a staff member clicks "Resend Nudge" Then a new nudge is sent only if at least 3 minutes have elapsed since the last nudge for that order And if the rate limit is not met, no SMS is sent and the UI shows "Try again in mm:ss" And successful resends update the "Nudged" chip timestamp without creating duplicate chips And all resend attempts (sent/blocked) are recorded in the activity log
Duplicate Near-Bay Event Merging
Given multiple near-bay detections occur for the same order within a 2-minute window When events are processed Then the Staff Console displays a single "Near Bay" chip with a counter (e.g., ×3) and last-seen timestamp And the activity log shows a grouped entry with the count and time range And no additional audible/visual alerts are triggered for merged events
Mark as Assisted Overrides Automation
Given a staff member is actively helping the guest outside automated nudging When the staff member clicks "Mark as Assisted" on the order Then the order displays an "Assisted" chip with timestamp and staff initials if available And any pending or scheduled nudges are canceled immediately And the order is excluded from Near-Bay attention queues and does not receive further nudges until returned to "Awaiting Pickup" or completed
Nudge Outcomes Analytics
"As an operator, I want to see how often nudges resolve misparks and how quickly, so that I can optimize layouts and staffing."
Description

Captures and reports key metrics for ParkRight Ping: near-bay event rate, nudge send/delivery/click-through, correction rate, median time-to-correct, and impact on overall wait and handoff times. Breaks down by location, time of day, and bay layout versions, with privacy-safe aggregation and CSV export. Adds coaching insights (e.g., bays with high mispark rates) to inform layout tweaks. Expected outcome: measurable validation of reduced corrections and faster, cleaner parking.

Acceptance Criteria
Track Near-Bay Event Rate
Given near-bay geofence and dwell thresholds are configured When a guest device is detected within the near-bay zone but not in the assigned bay for at least the configured dwell threshold during an active curbside order Then log a NearBayEvent with order_id, assigned_bay_id, detected_bay_id (if any), location_id, timestamp_local, timestamp_utc And increment the near-bay event counter for the active session And deduplicate multiple detections for the same order within the configured session window And display near-bay event rate = NearBayEvents / Arrivals for any selectable date range on the analytics dashboard And update the metric within the configured data freshness SLA
Measure Nudge Send/Delivery/Click-Through
Given ParkRight Ping nudges are enabled and a NearBayEvent is logged When the platform sends the nudge SMS Then record nudge_sent_at and provider_message_id And when a delivery receipt is received within the delivery receipt window, record nudge_delivered_at and set delivery_status=delivered; otherwise set delivery_status=failed or unknown after timeout And when the guest taps the unique "I've moved" link, record click_at and attribute the click to the originating nudge And display counts and rates by date range: Nudges Sent, Nudges Delivered, Clicks, CTR (Clicks/Delivered) and Clicks/Sent And update these metrics within the configured data freshness SLA
Calculate Correction Rate and Median Time-to-Correct
Given a nudge was sent for an order When the guest moves into the assigned bay and the system detects in-bay status or staff confirms correction within the configured correction window Then set correction=true and record corrected_at Else after the correction window elapses, set correction=false And compute and display Correction Rate = corrected_nudges / nudges_sent for the selected date range And compute and display Median Time-to-Correct for corrected_nudges in the selected date range And exclude orders without a sent nudge from these calculations
Report Impact on Wait and Handoff Times
Given a reporting date range and a selected location When computing impact metrics Then calculate median overall wait time ("I'm here" to handoff) for nudged orders and matched non‑nudged orders (matched by location, daypart, and bay layout version) And calculate median handoff duration (staff arrival to completion) for both cohorts And display delta_median_wait and delta_median_handoff with units and direction And suppress the comparison if either cohort has fewer than the configured minimum_sample_size, showing "Insufficient data" And update the metrics within the configured data freshness SLA
Segment by Location, Time of Day, and Bay Layout Version
Given dashboard filters for Location, Daypart/Hour, and Bay Layout Version When a user applies any combination of these filters Then recompute and display all metrics (near-bay rate, nudge metrics, correction rate, median time-to-correct, wait and handoff medians) for the selected segments And provide per-segment breakdowns with labels and sample sizes And use each location's local timezone for time-of-day segmentation
Privacy-Safe Aggregated CSV Export
Given a user with export permissions requests a CSV for a date range and selected segments When the export is generated Then include only aggregated rows per segment and exclude PII (e.g., phone numbers, SMS text, vehicle details, precise coordinates) And apply a k-anonymity threshold so rows with sample_size below the threshold are redacted And include columns: date_range, location_id, location_name, bay_layout_version, daypart_or_hour, arrivals, near_bay_events, near_bay_rate, nudges_sent, nudges_delivered, clicks, ctr_delivered, clicks_per_sent, corrections, correction_rate, median_time_to_correct, median_wait, median_handoff_duration And specify timestamps in UTC in metadata and clearly label any local-time columns And make the CSV available for download within the configured export SLA
Coaching Insights for High Mispark Bays
Given the reporting period meets the minimum sample size per bay When generating coaching insights Then identify bays whose near-bay event rate exceeds the location median by at least the configured threshold percentage And rank and display the top bays with count, rate, and delta vs median And attach actionable suggestions from the coaching library (e.g., signage or layout tweaks) And provide deep links to bay trends and allow users to mark insights as acknowledged or resolved And refresh insights on a scheduled weekly job and via an on-demand Recompute action

BayHeat Overlay

Adds a live heatmap to the arrivals board showing bay utilization, average dwell, and slow‑turn hotspots. Suggests temporary closures or preferred bays so Captains can steer cars where turnover is fastest and runners walk less.

Requirements

Real-time Bay Utilization Engine
"As a Captain, I want accurate, real-time bay occupancy and dwell metrics so that I can immediately see which bays are turning quickly versus stalling and act accordingly."
Description

Build a backend service that computes live occupancy, dwell time, and turnover metrics per bay by ingesting arrival events (“I’m here”), bay assignments, order handoff/completion, and manual overrides. Maintain per-bay state (open/closed/preferred/unavailable), handle unassigned arrivals safely, and calculate rolling aggregates over configurable windows (e.g., 15/30/60 minutes). Expose both a low-latency streaming feed (WebSocket/SSE) and a REST snapshot for the arrivals board and downstream consumers. Include clock-skew handling, idempotent event processing, and hysteresis to avoid metric jitter. The output is the authoritative metrics stream powering the heatmap and recommendation engine.

Acceptance Criteria
Real-time Occupancy & Dwell Metrics Computation
Given a bay has no active order When an "I'm here" event is ingested with an assignment to that bay Then the bay's occupancy status is set to occupied and a dwell timer starts based on normalized event time within 300 ms end-to-end Given a bay is occupied for an active order When a handoff or completion event for that order is ingested Then the bay's occupancy status is set to vacant within 300 ms and the completed dwellSeconds equals completionTime - arrivalTime within ±2 seconds Given a bay transitions from occupied to vacant due to completion When the next metrics update is emitted Then turnoverCount for that bay increments by 1 and is reflected in all configured rolling windows
Bay State Management with Hysteresis
Given a bay has a current state in {open, closed, preferred, unavailable} When a valid manual override is received for that bay Then the bay state updates to the specified value within 300 ms and persists until changed or its override TTL expires Given metric-driven thresholds that would cause frequent state or score oscillations When inputs fluctuate within ±1% of a threshold Then the engine applies hysteresis so that published values for that bay do not toggle more than once every 60 seconds and changes under a 2% delta are coalesced for at least 5 seconds (both values configurable) Given an invalid state transition request When processed Then the request is rejected and the prior state remains unchanged with a reason code emitted on the stream
Unassigned Arrivals Safety
Given an "I'm here" event arrives without a bay assignment When it is ingested Then it is recorded as unassigned, no bay's occupancy is altered, and the unassignedArrivalCount is incremented and emitted on the stream and snapshot Given an unassigned arrival later receives a bay assignment event within the lateness window When processed Then the bay occupancy start time is backfilled to the original arrival time (after clock normalization), dwell begins, and unassignedArrivalCount is decremented Given an unassigned arrival never receives an assignment When a timeout elapses (configurable) Then it is expired without affecting any bay metrics and an expiration event is emitted
Rolling Window Aggregates (15/30/60 minutes)
Given rolling window configuration of [15, 30, 60] minutes When arrivals, completions, and overrides occur Then per-bay aggregates include avgDwellSeconds, occupancyRate, and turnoverCount for each window and are updated within 500 ms of relevant events Given multiple completed orders with known dwell durations within a window When aggregates are computed Then avgDwellSeconds matches the arithmetic mean within ±2 seconds and turnoverCount matches the exact number of completions in-window Given the rolling window configuration is changed at runtime When new values are applied via configuration Then both stream and REST reflect the new windows within 5 seconds and old windows are no longer emitted
Idempotent and Out-of-Order Event Handling
Given duplicate events with the same eventId are delivered one or more times When processed Then the engine produces the same resulting state and metrics as a single delivery and emits at most one corresponding metrics update (idempotent) Given arrival, assignment, and completion events for an order are received out of chronological order within a 2-minute lateness tolerance When processed using eventTime ordering Then occupancy intervals and dwell are reconstructed correctly and aggregates reflect the corrected history Given events arrive later than the configured lateness tolerance When processed Then the engine uses receiveTime without violating invariants, emits a lateEvent flag, and does not double-count turnover or dwell
Clock Skew Normalization
Given client event timestamps may drift up to ±120 seconds from server time When events are ingested Then the engine normalizes eventTime per source so that computed dwell error is ≤ 2 seconds at p95 compared to server-synchronized time Given an event's clock skew exceeds ±120 seconds When processed Then the engine substitutes server receiveTime as eventTime and emits a clockSkewExceeded flag for that event and affected metrics updates Given a source's observed skew changes over time When sufficient samples are collected Then the per-source skew estimate is updated at least every 60 seconds and applied to subsequent events
Dual Interfaces: Streaming Feed and REST Snapshot
Given the streaming endpoints are connected (WebSocket and SSE) When bay metrics change Then p95 end-to-end delivery latency is ≤ 300 ms and p99 ≤ 800 ms for up to 100 orders/min and 50 bays, and each message includes a monotonic sequence/cursor Given an SSE client reconnects with Last-Event-ID or a WebSocket client reconnects with a cursor within 60 seconds When the connection is re-established Then missed updates are replayed exactly-once without duplication and a heartbeat is sent at least every 10 seconds Given a consumer requests GET /v1/bays/metrics When the snapshot is generated Then p95 response time is ≤ 150 ms for 50 bays and the payload includes for each bay: bayId, state, occupancy, dwellSeconds (if occupied), avgDwellSeconds{15,30,60}, turnoverCount{15,30,60}, lastUpdatedAt, and an ETag header Given no new events are processed for 1 second When comparing the latest stream state to the REST snapshot Then the values for all bays are identical
Heatmap Visualization Overlay
"As a Captain, I want a visual heatmap on the board so that I can assess bay performance at a glance without digging into numbers."
Description

Add a toggleable heatmap layer to the arrivals board that color-codes each bay by utilization/dwell, with tooltips showing current dwell, rolling averages, turnover rate, and status (open/closed/preferred). Provide a clear legend and adaptive color scale with accessible contrast (WCAG AA). Ensure low-latency updates (<2s), smooth transitions without flicker, and responsive layout across tablets, laptops, and wall displays. Support dark mode, per-user sticky toggle state, and graceful degradation if the metrics stream is unavailable (fallback to last-known snapshot with timestamp).

Acceptance Criteria
Overlay Toggle with Per-User Sticky State
Given a staff user on the Arrivals Board, When the user toggles the Heatmap overlay on/off, Then the overlay visibility changes within 300 ms without shifting underlying bay layout. Given the user has set a heatmap toggle state, When the user reloads the page or starts a new session, Then the last chosen state is applied for that user account. Given keyboard-only navigation, When focus is on the toggle, Then Space/Enter toggles the overlay and the toggle announces its state via ARIA.
Adaptive Heatmap Rendering and Legend Accessibility
Given bays with utilization and dwell data available, When the heatmap is enabled, Then each bay is color-coded using an adaptive scale derived from the current data range. Given the heatmap is visible, Then a legend is displayed with labeled numeric ranges and units that match the active color scale. Given light or dark theme, Then legend text and bay labels meet WCAG 2.1 AA contrast (>= 4.5:1) and are readable without relying solely on color.
Tooltip Data Completeness and Accessibility
Given a bay is focused, hovered, or tapped, When the heatmap is enabled, Then a tooltip appears within 200 ms and shows current dwell, rolling average dwell, turnover rate, and bay status (open/closed/preferred) with units. Given keyboard navigation, When a bay receives focus, Then the same tooltip content is available and dismissible with Escape. Given screen readers, When the tooltip opens, Then its content is announced with accessible names for each metric.
Low-Latency Updates and Smooth Transitions
Given live metric updates are emitted, When new data arrives, Then the corresponding bay colors and tooltip values update on-screen within 2 seconds (95th percentile). Given a visual change in heat intensity, When the color updates, Then the transition animates over 150–400 ms with no flicker or blank intermediate frames.
Responsive Layout Across Target Displays
Given supported displays (tablet, laptop, wall display), When the viewport is 1024x768, 1366x768, 1920x1080, or 3840x2160 in landscape or portrait, Then bays render without overlap, legend does not obscure bays, and content remains readable. Given touch devices, When interacting with bays or the legend, Then all hit targets are at least 44x44 CSS pixels.
Graceful Degradation on Metrics Stream Outage
Given the metrics stream becomes unavailable, When the outage is detected, Then the heatmap remains visible using the last-known snapshot and shows a "Last updated" timestamp. Given the stream recovers, When new data arrives, Then the heatmap automatically resumes live updates within 2 seconds and removes any stale-data warning.
Dark Mode Compatibility and Contrast AA
Given dark mode is enabled, When the heatmap is visible, Then background, bays, legend, and tooltip palettes switch to dark variants while maintaining WCAG 2.1 AA text contrast (>= 4.5:1). Given dark mode is enabled, When the adaptive color scale is applied, Then adjacent heat levels remain distinguishable for common color-vision deficiencies and the legend reflects the dark palette.
Hotspot Detection and Bay Recommendations
"As an operator, I want the system to highlight slow-turn bays and suggest better alternatives so that I can keep turnover high and reduce runner walking distance."
Description

Implement a rules-based engine that flags slow-turn hotspots (e.g., sustained dwell above threshold and high utilization) and identifies fast-turn or shorter-walk bays as preferred. Generate human-readable recommendations to temporarily close problematic bays or prefer faster ones, including an explanation and confidence score. Apply hysteresis to prevent flapping, set expiry times for recommendations, and expose an API/UI surface for consumption. Integrate with the utilization engine and heatmap to visually annotate hotspots and suggested actions.

Acceptance Criteria
Detect Slow‑Turn Hotspot Based on Dwell and Utilization
Given location L with configured thresholds dwellThreshold=7m, utilizationThreshold=80%, sustainWindow=10m and the engine subscribed to the utilization/dwell data stream And bay B has rollingAvgDwell >= 7m for the last 10m and utilization >= 80% for the last 10m When the rules engine evaluates conditions Then bay B is flagged as a hotspot within 10s of the sustainWindow being satisfied And a recommendation is published with action="temporary_close", bayId=B, locationId=L, explanation including B, rollingAvgDwell, utilization, thresholds, time window, and confidence in [0,100] with confidence >= 70 And no recommendation is emitted for bays that do not meet both thresholds within the sustainWindow
Prefer Fast‑Turn or Short‑Walk Bays
Given configured fastTurn thresholds fastDwellThreshold=3m, shortWalkThreshold=50m, sustainWindow=10m and a walkDistance map per bay And a set of candidate bays C that are not hotspots or temporarily closed When the engine evaluates candidates every 60s Then it emits up to the top 3 "prefer" recommendations for bays in C where rollingAvgDwell <= 3m over the last 10m or walkDistance <= 50m And the recommendations are ranked by rollingAvgDwell ascending then walkDistance ascending And each recommendation includes bayId, action="prefer", explanation citing dwell and/or distance metrics, and confidence >= 60 And no bay currently marked hotspot or closed is recommended as "prefer"
Hysteresis and Anti‑Flapping for Recommendations
Given antiFlapWindow=5m, exitDwellThreshold=5m, exitUtilizationThreshold=60%, exitWindow=5m And an active hotspot recommendation for bay B When dwell and utilization fluctuate around thresholds Then the hotspot remains active until rollingAvgDwell < 5m AND utilization < 60% continuously for 5m And no bay may change recommendation action (none|prefer|temporary_close) more than once per 5m And extending a recommendation’s expiry (refresh) is allowed within the antiFlapWindow without changing its action
Recommendation Expiry and Auto‑Refresh
Given recommendationTTL=15m and refreshLeadTime=2m When a recommendation R is created at time t0 Then R includes createdAt=t0, expiresAt=t0+15m, and state="active" And if trigger conditions persist at or after t0+13m, the engine refreshes R by extending expiresAt by +15m without creating a new recommendation id And if trigger conditions cease and no refresh occurs, R transitions to state="expired" at expiresAt and is no longer returned by the API within 5s And active recommendations never present an expiresAt in the past
Recommendations API Contract
Given GET /v1/locations/{locationId}/recommendations?state=active When called with a valid locationId Then respond 200 within p95<=300ms with a JSON array of items each containing: id(UUID), locationId, bayId, action(enum: temporary_close|prefer), explanation(string<=280 chars), confidence(int 0..100), createdAt(ISO‑8601), expiresAt(ISO‑8601), state(enum: active|expired), source("rules-engine"), version(semver) And the default state filter is active; when state=all is provided, both active and expired are returned with correct state values And items are sorted by confidence desc, then createdAt desc And requests with unknown locationId return 404
Heatmap Overlay Annotation of Hotspots and Preferred Bays
Given the arrivals board with BayHeat overlay enabled and an active recommendation for bay B When action=temporary_close Then the heatmap overlays B with a red outline and a visible "Hotspot • Temp close" chip; on hover/tap a panel shows the explanation and confidence When action=prefer Then the heatmap highlights B in green with a "Prefer" chip; on hover/tap a panel shows the explanation and confidence And UI reflects API changes within 5s and does not visually toggle a bay’s action more than once per 5m (per anti‑flapping)
Insufficient Data Safeguards and Confidence/Explanation Quality
Given minSamplesPerWindow=10 for the sustainWindow When a bay has fewer than 10 dwell/utilization samples in the sustainWindow Then no hotspot or prefer recommendation is emitted for that bay and the API omits any recommendation for it And every emitted recommendation includes confidence in [0,100]; when metrics are exactly at thresholds, confidence is between 50 and 65; when both hotspot metrics exceed thresholds by >=20%, confidence is >=80 And every explanation is human‑readable (40–280 chars) and must reference the bay identifier and the key metrics that triggered the rule and the suggested action
Captain Controls for Bay Management
"As a Captain, I want quick controls to close or prefer bays so that I can react to on-the-ground conditions without leaving the arrivals board."
Description

Provide on-board controls to manually open/close bays, mark bays as preferred, accept or dismiss system recommendations, and time-box those changes (e.g., close for 20 minutes). Enforce role-based permissions, record an audit trail of changes, and instantly propagate updates to the heatmap, assignment logic, and metrics engine. Include inline feedback (e.g., countdown timers for temporary closures) and safeguards to prevent assigning closed bays to incoming customers.

Acceptance Criteria
Time-Boxed Bay Closure with Countdown and Auto-Reopen
Given I am a Captain on the arrivals board with at least one open bay When I close Bay N and set a 20-minute timer Then Bay N changes to Closed within 1 second on my client and within 2 seconds on all connected clients And a visible countdown timer appears on Bay N showing remaining time in minutes/seconds And Bay N is immediately excluded from auto-assignment and customer self-selection flows And the closure reason and duration are captured When the timer expires Then Bay N auto-reopens, the countdown disappears, and Bay N becomes eligible for assignment within 2 seconds When I manually reopen Bay N before expiry Then the timer stops, Bay N opens immediately, and remaining duration is logged
Preferred Bay Marking Influences Assignment and Overlay
Given at least two eligible open bays When I mark Bay X as Preferred (optionally with a 30-minute timer) Then the BayHeat overlay displays a Preferred indicator on Bay X within 2 seconds for all clients And assignment logic prefers Bay X over non-preferred bays when all other eligibility factors are equal And in a controlled test with 20 consecutive equal-eligibility arrivals, at least 90% are assigned to a Preferred bay when one is available When the timer (if set) expires or I remove the preference Then the Preferred indicator is removed within 2 seconds and assignment bias ceases
Recommendation Handling (Accept/Dismiss) with Applied Effects
Given the system surfaces a recommendation (e.g., Close Bay 3 for 15 minutes due to slow-turn hotspot) When I accept the recommendation Then the recommended action is applied with the suggested duration and reason tagged as Recommendation Accepted And countdowns, indicators, heatmap, and assignment logic update within 2 seconds for all clients And the acceptance is recorded in the audit trail with recommendation id and inputs snapshot When I dismiss the recommendation and optionally provide a reason Then no bay state changes are applied, the recommendation is hidden from my board, and the dismissal is logged with reason And the same recommendation id is not re-shown to me for at least 10 minutes unless its parameters materially change
Role-Based Controls Enforcement
Given role-based permissions are configured with at least Captain and Viewer roles When a Viewer attempts to close/open a bay, mark Preferred, or act on a recommendation Then the action is blocked with an Insufficient Permissions message and no state change occurs When a Captain performs the same actions Then the action succeeds subject to validation and propagates within 2 seconds to all clients And all authorization decisions are enforced server-side; direct API calls without proper scope receive HTTP 403 and are logged
Instant Propagation to Heatmap, Assignment, and Metrics
Given any bay state change (open, close with timer, preferred on/off) When the change is saved Then the BayHeat overlay reflects the new state within 2 seconds (p95) and 3 seconds (p99) during business hours And the assignment engine uses the new state for the next incoming arrival decision And the metrics engine records the state change with timestamps such that utilization and dwell exclude Closed intervals and attribute Preferred intervals And no stale clients display a conflicting state for more than 5 seconds
Comprehensive Audit Trail of Bay State Changes
Given audit logging is enabled When any bay control change occurs (open/close/preferred on/off, accept/dismiss recommendation) Then a single immutable audit record is created containing: timestamp (UTC), actor id, actor role, action type, bay id, previous state, new state, duration (if any), reason/source (UI/API/recommendation id), and request id And audit records are available via UI log view within 5 seconds and via API within 5 seconds of the action And attempting to modify or delete an audit record is blocked and logged And exporting a day’s audit records returns a complete, chronologically ordered dataset with no gaps
Assignment Safeguards for Closed Bays and Reassignment
Given Bay Y is Closed When a new arrival triggers assignment Then Bay Y is excluded from consideration, and no customer or staff instruction references Bay Y When a bay is closed after an order was pre-assigned to it but before the customer is notified Then the system reassigns to the next best eligible bay and only the final bay is communicated When a bay is closed after the customer has been notified Then the system immediately reassigns to an eligible bay and sends a single follow-up SMS updating the bay within 5 seconds, and the arrivals board reflects the change And all prevented assignments and reassignments are logged with reasons
Auto-Reply Bay Guidance via SMS
"As an arriving customer, I want a clear SMS telling me which bay to use so that I can park confidently and staff can find me faster."
Description

When a customer taps the “I’m here” link, automatically select an available recommended bay based on current closures, preferences, and hotspot signals, then send an SMS instructing the driver where to park. Reserve the assigned bay for a short window to avoid collisions, release it on arrival confirmation or order handoff, and provide fallbacks if the lot is full (e.g., queue instructions). Support multi-language templates, location-specific copy, and manual override by Captains. Ensure the guidance logic respects Captain-set closures and preferences in real time.

Acceptance Criteria
Auto-select recommended bay and send SMS on arrival
Given a customer taps the "I'm here" link for an active order When the system processes the arrival event Then select an available bay that is not closed and not reserved, prioritizing preferred bays and heatmap fast-turn suggestions And send an SMS with the assigned bay identifier and parking instructions within 5 seconds of the tap And create a reservation hold on the bay for the configured window (default 5 minutes, configurable 1–10 minutes) And persist the assignment and reservation state in the arrivals board
Real-time respect of Captain-set closures and preferences
Given a Captain updates bay statuses (close/open, mark preferred) from the board When the update is saved Then the guidance logic must exclude closed bays from assignment immediately and prioritize preferred bays And the new status is applied to all assignment decisions within 2 seconds of the change And in-flight assignment requests started before the change are revalidated before send; if invalid, a different valid bay is selected
Bay reservation window prevents collisions
Given a bay is assigned to a customer When a second arrival occurs during the reservation window Then the reserved bay must not be assigned to the second customer And the second customer is assigned a different valid bay or handled via fallback And concurrent assignments across multiple arrivals produce no duplicate bay reservations And the reservation window expires automatically at the configured time if no arrival confirmation is received
Release bay on arrival confirmation or handoff
Given a bay is reserved for a customer When the customer confirms arrival/parked via the link or a Captain marks the order handed off Then release the bay to available within 1 second And clear the customer's reservation and assignment state And stop any pending fallback or re-guide timers for that customer And update the arrivals board and API within 1 second
Fallback queue instructions when bays unavailable
Given all physical bays are closed or reserved at the time of an arrival When no valid bay can be assigned Then place the customer in a virtual queue and send an SMS with queue position, wait estimate, and hold instructions within 5 seconds And notify Captains on the board that the lot is full and a queue has started And re-evaluate availability at least every 15 seconds and upon any bay release And limit customer SMS updates to no more than one per 60 seconds until an assignment is available And upon bay availability, assign and SMS the bay within 5 seconds and start a reservation hold
Multi-language and location-specific SMS templates
Given a location has SMS templates for multiple languages and custom copy When generating the guidance SMS Then select the template language that matches the customer's stored locale or message thread language; if none, use the location default And include location-specific variables (bay label, address, pickup instructions) without exceeding 320 GSM-7 characters or 160 UCS-2 characters And include "Reply STOP to opt out" per carrier policy And render without unresolved placeholders and pass template validation; otherwise fall back to the default template and log the error
Captain manual override of bay assignment via board
Given a Captain manually overrides an auto-assigned bay for an active order When the override is confirmed Then send an updated SMS with the new bay within 5 seconds And release the prior bay and reserve the new bay immediately And prevent override to closed or reserved bays; if attempted, show error and do not change assignment And write an audit log with Captain ID, timestamp, old bay, new bay, and reason (optional)
Bay Map and Threshold Configuration
"As a location admin, I want to configure my bay layout and heat thresholds so that the heatmap and recommendations match my lot and operational goals."
Description

Create an admin configuration surface to define bay count, labels, and spatial layout used by the overlay, plus global and per-location thresholds for color scaling, hotspot detection, and rolling window durations. Allow tuning of preference heuristics (e.g., weight fast turnover vs. shorter runner distance), with safe defaults. Validate configurations to prevent overlapping or invalid bay definitions, version changes safely, and propagate updates without downtime to all connected boards and guidance logic.

Acceptance Criteria
Admin Configures Bay Count, Labels, and Spatial Layout
Given I am an admin on location L with edit permissions When I set bay count to N ≥ 1, assign unique labels for each bay, and draw non-overlapping polygons within the lot boundary for all bays Then Save is enabled only when validation passes and returns 201 Created with version V And The board renders the new bay map for location L within 5 seconds of save And Bay labels and indices on the board match the configuration 1:1 And Attempting to define overlapping polygons, duplicate labels, or bays outside bounds blocks save and shows inline errors per field
Global and Location Thresholds for Heatmap Scaling and Hotspots
Given global defaults for color scale min/max, hotspot dwell threshold (seconds), and slow-turn percentile exist When I configure overrides for location L leaving some fields unset Then effective thresholds for L use overrides where provided and fall back to globals otherwise And Inputs are validated: min < max, values within allowed ranges, seconds are integers And The heatmap and hotspot detection on location L reflect the effective thresholds within 5 seconds of save
Preference Heuristic Weights Tuning and Validation
Given the guidance heuristic supports weights for fast turnover and runner distance When I set weights such that each ∈ [0,1] and at least one > 0 Then weights are normalized to sum to 1 for computation and persisted as entered And Invalid inputs (negative, NaN, all zeros) are rejected with specific messages And Suggested bay ordering in guidance for new arrivals updates within 5 seconds and is reproducible given the same inputs
Safe Versioning, Audit Trail, and Rollback of Configurations
Given a previous configuration version V exists for location L When I save a new configuration Then a new immutable version V+1 is created with timestamp, author, and diff summary And I can select V in the history and rollback And All connected clients switch to the rolled-back version within 5 seconds without disconnects And The audit log records save and rollback events
Live Propagation of Config Updates Without Downtime
Given arrival boards and guidance services are connected to location L When a configuration is saved successfully Then connected clients receive the update via push within 5 seconds And No active pickup sessions are dropped or require a page reload And For any client temporarily offline, the new config is applied on reconnect before rendering
Rolling Window Duration Configuration and Effect on Metrics
Given the rolling window duration controls the averaging period for dwell and utilization When I set the window to 15, 30, or 60 minutes (or any value within 5–180 minutes) Then inputs outside the range are rejected; valid inputs are persisted And The heatmap color scaling and average dwell on the board reflect the new window within 5 seconds And The metrics endpoint returns values computed over the configured window
Multi-Location Overrides and Fallback Behavior
Given an organization with global defaults and multiple locations L1 and L2 When I update a global default that L1 has not overridden Then L1 reflects the new global default and L2 remains unchanged for fields it has overridden And I can reset a location field to “Use Global” and the effective value updates accordingly And The UI clearly indicates per-field whether the effective value is global or overridden

Layout Wizard

Guides operators through mapping bays on a satellite or lot photo, numbering spots, setting the staging door, and calibrating geofences with a quick SMS test drive. Accurate layouts mean smarter autocompletes from day one—even for pop‑ups.

Requirements

Map & Annotate Pickup Lot
"As an operator, I want to map and label bays on a photo or satellite view so that staff can see a precise pickup layout without installing hardware."
Description

Provide a mobile-first wizard that lets operators import a satellite map centered on their address or upload a recent lot photo, then draw and edit pickup bays as shapes (rectangles/polygons), name them, and place landmarks (entrances, medians, accessible spots). Include pan/zoom, snap-to-angle, drag-to-reorder, undo/redo, and autosave drafts. Persist precise geo-coordinates per bay and lot bounds, optimized for low-bandwidth connections. Store the layout in the location profile and expose a read-only overlay in the staff dashboard. Support mixed lighting images and orientation lock to align uploaded photos with north. Ensure accessibility (contrast, keyboard navigation) and secure access via role-based permissions. Changes should not affect live detection until explicitly published.

Acceptance Criteria
Import Map or Photo and Align to North
Given a location profile with address coordinates When the operator selects "Use satellite map" Then the map initializes centered within 15 meters of those coordinates and at a zoom level between 17 and 19 Given a valid JPEG/PNG between 0.5 MB and 25 MB When the operator uploads a lot photo Then a low‑res placeholder appears within 500 ms on a 1 Mbps connection and the full image renders within 2 seconds Given a loaded map or photo When the operator enables Orientation Lock and confirms "Align to North" Then the canvas rotates so the top is within 5° of true north and the rotation state persists across sessions until changed Given any background imagery When overlays (bay outlines, labels) render Then all overlay strokes and text meet WCAG 2.2 AA contrast (≥ 4.5:1) against the underlying pixels at their positions
Draw, Edit, and Name Pickup Bays with Undo/Redo and Autosave
Given the canvas is ready When the operator draws a rectangle with Shift held Then edges snap to 0/90/180/270 degrees with ±5° tolerance and corner handles are visible for adjustment Given polygon mode is active When the operator places vertices and closes the shape by tapping the first vertex or double‑tapping Then a non‑self‑intersecting polygon bay is created and stored Given a new or edited bay When the operator enters a name Then the name is required, unique within the layout, and limited to 1–24 alphanumeric characters plus spaces; invalid input shows an inline error and blocks save Given existing bays When the operator drags a bay or its vertices Then geometry updates smoothly with sub‑meter precision and snap‑to‑angle is available when Shift is held Given any change (draw, move, rename, delete, reorder) When 1 second has elapsed or focus leaves the canvas Then a draft autosave occurs and a "Saved" indicator appears Given a sequence of edits in the current session When the operator uses undo/redo Then the last 50 operations are undoable/redoable in order Given the page is reloaded after autosave When the wizard reopens Then the last autosaved draft is restored with no loss of drawn bays or names
Place and Tag Landmarks (Entrances, Medians, Accessible Spots)
Given the landmarks palette is visible When the operator selects Entrance, Median, or Accessible Spot and clicks/taps the canvas Then the landmark is placed with a geo‑coordinate, is draggable, and can be deleted Given an Accessible Spot landmark exists When it is rendered Then it displays the standard accessibility icon and carries accessible=true metadata Given no Entrance landmarks are defined When the operator attempts to publish Then validation fails with the message "At least one entrance is required" and publish is blocked Given keyboard‑only navigation When the operator uses Tab/Enter/Arrow keys per on‑screen help Then they can place, move, and delete landmarks without a mouse and all actions are reachable via keyboard
Persist Precise Geo and Lot Bounds
Given at least one bay is drawn and lot bounds are defined When the draft is saved Then the lot boundary polygon and each bay geometry are stored in WGS84 with ≥ 6 decimal places and with no self‑intersections Given a saved draft exists When it is reloaded Then all geometries, names, and display order reappear identically with coordinate differences ≤ 0.000001° and no missing entities Given geometries are edited When the operator saves again Then updated_at timestamps change and version is incremented without creating duplicate bays
Low‑Bandwidth Performance and Resilience
Given a 1 Mbps downlink and 100 ms RTT When the wizard loads cold Then time‑to‑interactive is ≤ 3 seconds and total transfer size is ≤ 800 KB excluding map tiles or uploaded photo Given imagery is loading When the operator pans or zooms Then UI event latency remains ≤ 100 ms and imagery streams progressively without blocking interactions Given autosave under 5% packet loss When a change occurs Then the client retries with exponential backoff up to 5 attempts and displays Saving/Saved/Offline status appropriately without user data loss Given a 30‑second connection loss during editing When the connection is restored Then the last local draft is synchronized automatically with no duplicate bays or landmarks created
Publish Workflow and Role‑Based Permissions
Given user roles Owner or Manager When they access the layout wizard Then edit and Publish controls are visible; Given role Staff or lower, Then only read‑only view is available and unauthorized edit/publish API calls return HTTP 403 Given unpublished draft changes exist When an operator clicks Publish and confirms Then the live detection configuration updates to the new layout version; until publish, live detection continues using the previous published layout Given a publish action completes When viewing version history Then the new version is listed with timestamp, publisher, and a change summary including counts of added/removed/modified bays and landmarks Given a location without defined lot bounds When publish is attempted Then publish is blocked with the message "Define lot bounds before publishing" Given staff dashboards are open When a publish event occurs Then overlays update within 30 seconds or on next page load, whichever comes first
Staff Dashboard Read‑Only Overlay
Given a published layout exists When a Staff user opens the dashboard map Then the layout overlay renders aligned with geospatial offset ≤ 3 meters across the current viewport Given the read‑only overlay When a bay is tapped or focused Then a detail card shows bay name and number without any edit controls Given the read‑only overlay When a landmark is tapped or focused Then a detail card shows the landmark type (Entrance, Median, Accessible Spot) without any edit controls Given the user toggles the overlay visibility When the dashboard is reloaded on the same device/browser Then the on/off preference persists for that user Given standard (1x) and high‑DPI (2x+) displays When the overlay is rendered Then text size is ≥ 12 px and line strokes are ≥ 2 px for readability
Bay Numbering & Validation
"As an operator, I want automatic, flexible bay numbering with validation so that customers and staff have clear, error-free directions."
Description

Enable auto-numbering of drawn bays with configurable sequences (1..N, A..Z, custom labels), bulk renumbering, and gap/dedup detection. Provide visual validation warnings for duplicates, missing numbers, or ambiguous labels, and enforce uniqueness on publish. Allow reserved tags (e.g., ADA, staff-only) and color-coding. Support directional hints (e.g., left-to-right) and printable bay placards export. Persist numbering rules so new bays inherit the correct scheme. Integrate with customer SMS links and staff UI to ensure the same labels appear everywhere.

Acceptance Criteria
Auto-numbering with Configurable Sequences and Directional Hints
Given a layout with N drawn bays and a selected numbering sequence (Numeric 1..N, Alphabetic A..Z, or Custom List) And a selected orientation (Left-to-Right, Right-to-Left, Top-to-Bottom, Bottom-to-Top) When Auto-Number is applied Then each bay receives a unique label from the sequence according to the orientation And labels render on the canvas and in the bay list immediately And custom list labels are applied in the provided order And leading/trailing whitespace is trimmed and case is preserved
Bulk Renumbering with Preview and Undo
Given a labeled layout When the operator invokes Bulk Renumber, selects a sequence and orientation, and clicks Preview Then a non-destructive overlay shows proposed labels for all bays And on Apply, existing labels are replaced with the proposed labels And an Undo action restores the previous labels in one step And bay geometry and IDs remain unchanged
Validation Warnings and Uniqueness Enforcement
Given the layout contains duplicate labels, gaps, or ambiguous labels (case-insensitive duplicates) When validation runs (on change and on Publish) Then affected bays display inline warnings specifying the issue type (Duplicate, Missing, Ambiguous) And a validation panel lists all current issues with click-to-focus each bay And Publish is blocked while any critical errors remain And resolving issues clears warnings and enables Publish
Reserved Tags and Color-Coding Behavior
Given a bay supports reserved tags (e.g., ADA, Staff-Only) with associated colors/icons When a reserved tag is applied to a bay Then the bay displays the corresponding color and icon per legend And label uniqueness validation ignores reserved tags And reserved tags persist through auto-number and bulk renumber operations And reserved tags appear in staff UI and exports, and are hidden from customer SMS selection unless enabled
Persisted Numbering Rules and Inheritance for New Bays
Given an active numbering rule (sequence type, start/custom list, orientation) is set for a layout When a new bay is drawn or an existing bay is duplicated Then the system assigns the next available unique label per the rule, filling the lowest gap before incrementing And the active numbering rule persists across sessions for the location And deleting a bay frees its label for future assignment but does not auto-shift other labels
Cross-Channel Label Consistency (SMS and Staff UI)
Given a published layout with finalized bay labels When a customer uses the SMS "I'm here" link and staff view arrivals in the Staff UI Then the bay labels presented to the customer match exactly (case and punctuation) the labels in the Staff UI And customer-facing auto-complete only accepts existing labels And the selected label in SMS displays identically in the staff arrival card and order logs
Printable Bay Placards Export
Given a published layout When the operator exports bay placards Then a vector PDF is generated with one placard per bay including the exact bay label and any reserved tag icon/color And the export supports Letter and A4 sizes with portrait/landscape options and safe margins And file name includes the location name and date stamp And text remains selectable in the PDF (no rasterized labels)
Staging Door Placement & Route Hints
"As a shift lead, I want to set the staging door and see route hints to bays so that runners get orders to cars faster."
Description

Allow operators to place one or more staging door markers on the layout and select a default. Compute straight-line and path-aware estimates to each bay using lot geometry to provide runner route hints and prioritization in the staff dashboard. Support multiple doors for large sites and show nearest door per bay. Expose API fields for door-to-bay distance to inform batching/dispatch logic. Include simple tooltips on the map for staff showing bay orientation and nearest aisle/landmark.

Acceptance Criteria
Place Multiple Staging Doors and Select Default
Given an operator is in Layout Wizard on a saved lot map, When they add 3 staging door markers with unique names, Then all 3 doors are persisted with lat/lng within ±1m of placement Given multiple doors exist, When the operator marks one as Default, Then the default door is visually indicated and stored on save Given a default door is set, When that door is deleted, Then the system prompts to choose a new default and prevents save until a default is selected Given door names must be unique per site, When a duplicate name is entered, Then the UI blocks save with a clear inline error Given a door is moved, When the operator saves, Then door-to-bay distances are re-computed for all bays within 2 seconds for up to 200 bays
Compute Straight-Line and Path-Aware Door-to-Bay Distances
Given a layout with doors, bays, aisles, and obstacles defined, When distances are computed, Then each bay has for each door: straight_line_m and path_aware_m in meters as non-negative numbers Given the lot graph reflects traversable paths, When path-aware distance is computed, Then it equals the shortest traversable path length within ±5% or 2m (whichever is greater) Given a bay or door is edited or moved, When the change is saved, Then all affected distances are recalculated and timestamped within 2 seconds for up to 200 bays Given geometry is invalid or disconnected, When a path-aware route cannot be found, Then path_aware_m is null and a warning is logged without blocking save Given two doors are equidistant by path-aware within 1m, When nearest-door is requested, Then tie-breaking uses lowest door_id and is flagged as tie:true
Nearest Door Assignment Per Bay
Given multiple doors exist, When viewing the layout, Then each bay displays its nearest door by path-aware distance and the distance value in meters Given a door is added, removed, or moved, When the layout is refreshed or saved, Then nearest door labels for bays update within 2 seconds Given path-aware is unavailable for a bay, When nearest door must be determined, Then straight-line distance is used and the label indicates SL fallback Given two doors are within 1m path-aware distance of a bay, When displaying nearest door, Then a tie indicator is shown in the bay tooltip
Runner Route Hints and Prioritization in Staff Dashboard
Given a default staging door is set, When orders are active and assigned to bays, Then the dashboard lists orders sorted ascending by path-aware distance from the default door Given a staff member selects a different door context filter, When viewing orders, Then route hints and sort order recalculate using that door within 1 second Given an order’s bay changes, When the update is received, Then its route hint (door name, distance, and ETA at 1.2 m/s walking speed) updates within 1 second Given two orders are within 3m of equal distance, When prioritizing, Then tie-breaking uses oldest ready_at timestamp first Given path-aware is unavailable, When displaying route hints, Then show straight-line distance with an SL badge and exclude ETA
API: Expose Door-to-Bay Distance Fields
Given a GET /v1/sites/{site_id}/bays/{bay_id} request, When the bay has doors defined, Then the response includes door_distances:{[door_id]:{straight_line_m:number, path_aware_m:number|null}} and nearest_door_id Given a GET /v1/orders/{order_id} request, When the order is assigned to a bay, Then the payload includes default_door_id, default_door_distance_m, and nearest_door_id Given no doors exist for a site, When requesting these endpoints, Then door distance fields are present as empty objects or nulls without 4xx/5xx Given the schema version is v1, When fields are added, Then existing clients continue to parse responses (non-breaking, fields are optional) Given distances are recalculated, When a client fetches data, Then updated_at timestamps reflect changes with second-level precision
Staff Map Tooltips: Bay Orientation and Landmark/Aisle
Given a staff user hovers or taps a bay on the map, When the tooltip appears, Then it shows bay number, orientation (compass bearing ±5°), nearest aisle/landmark name within 25m, and nearest door name Given no landmark is within 25m, When displaying the tooltip, Then the landmark field shows None and does not error Given a tooltip is opened, When the user navigates the map, Then the tooltip updates position and content within 200 ms if the bay selection changes Given accessibility needs, When tabbing through bays, Then the tooltip content is announced via ARIA live region and is keyboard navigable
Geofence Calibration via SMS Test Drive
"As an operator, I want to calibrate geofences via an SMS test drive so that arrival detection is accurate for my lot."
Description

Provide a guided calibration that sends an SMS link to a test phone. When opened, the browser collects foreground GPS samples during a short drive that enters, dwells, and exits target bays. Use collected traces to compute per-bay arrival radius, entry corridors, dwell thresholds, and hysteresis to reduce false positives from pass-through traffic. Present a readiness score and recommended thresholds, with one-click apply/rollback. Handle low-GPS environments with fallback heuristics and prompt operator to retry if sample quality is poor. Store calibration per location with timestamp and tester metadata, respecting privacy and consent prompts.

Acceptance Criteria
SMS Consent and GPS Sampling Start
Given the operator enters a valid test phone number and clicks "Send Test SMS" from the calibration screen When the request is submitted Then an SMS containing a calibration link is delivered to the test phone within 60 seconds Given the tester taps the SMS link and the calibration page opens in the foreground When the tester grants browser location permission Then GPS sampling starts within 3 seconds at a rate of ≥1 Hz and records latitude, longitude, timestamp, accuracy (m), speed, and heading Given the tester denies location permission When the page displays the rationale and retry controls Then no samples are stored and the run remains in a "Not started" state until permission is granted
Bay Entry, Dwell, and Exit Trace Collection
Given the operator has selected one or more target bays for calibration When the tester drives into a selected bay Then at least one entry segment is recorded with speed > 2 m/s and at least 10 samples with accuracy ≤ 25 m within 25 m of the bay center Given the vehicle is stationary in the bay When the dwell occurs Then a dwell segment is recorded with speed ≤ 0.5 m/s lasting ≥ 15 seconds and at least 15 samples with accuracy ≤ 25 m Given the vehicle exits the bay When the tester drives away Then an exit segment is recorded with speed > 2 m/s and at least 10 samples with accuracy ≤ 25 m Given a bay lacks any of the required segments or minimum-quality samples When the run completes Then that bay is labeled "Needs more data" and is excluded from apply by default
Parameter Computation and One-Click Apply/Rollback
Given the collected traces meet minimum quality thresholds for at least one bay When calibration is processed Then per-bay arrival radius (m), entry corridor polygon, dwell threshold (s), and hysteresis distance/time are computed and displayed Given computed parameters are available When the readiness score (0–100) is generated with per-bay breakdown Then bays scoring ≥ 80 are labeled "Ready" and bays < 80 are labeled "Needs more data" Given the operator clicks "Apply" When the configuration is saved Then the recommended thresholds are persisted to the location and become active within 5 seconds, with visible confirmation Given an applied configuration exists When the operator clicks "Rollback" Then the immediately prior configuration is restored within 5 seconds and the readiness display updates accordingly
False-Positive Suppression via Hysteresis
Given a pass-through segment travels within 10 m of a bay without a dwell segment (no period ≤ 0.5 m/s lasting ≥ 10 seconds) When the collected traces are simulated against the recommended thresholds Then no arrival event is predicted for that bay Given the full calibration trace set includes both dwell and pass-through segments When false-positive rate is computed on pass-through segments Then the rate is ≤ 2% with the recommended hysteresis and dwell thresholds
Low-GPS Fallback and Retry Flow
Given a calibration run is in progress When fewer than 60% of samples have accuracy ≤ 25 m or there are more than 3 consecutive gaps > 5 seconds Then the run is flagged "Poor sample quality" and the operator is prompted to retry Given a run is flagged "Poor sample quality" When fallback recommendations are generated Then the recommended dwell threshold is increased by at least 10 seconds and the arrival radius is increased by at least 20%, and these adjustments are clearly indicated Given the operator views a poor-quality run When "Apply anyway" is selected Then a confirmation step is required before thresholds can be applied
Privacy, Consent, and Calibration Data Storage
Given the tester opens the calibration page When the consent prompt is displayed Then sampling does not begin until the tester explicitly accepts, and a clear decline option is available Given the operator applies a calibration When data is stored Then the system saves the calibration per location with UTC timestamp, tester metadata (masked phone number last 4 digits and browser user agent), and a consent receipt reference Given raw GPS traces from a calibration run exist When the operator selects "Delete Run" Then raw traces and associated tester metadata for that run are deleted within 60 seconds while keeping the derived thresholds
Multi-Bay Coverage and Extrapolation for Unvisited Bays
Given a location has N bays configured When a calibration run completes Then each bay is listed with status: Calibrated (visited), Extrapolated (unvisited but inferred), or Needs more data (insufficient quality) Given one or more bays are unvisited When thresholds are recommended Then unvisited bays receive extrapolated thresholds using nearest calibrated bays (inverse-distance weighting) and are assigned a readiness score at least 20 points lower than the nearest calibrated bay Given extrapolated bays are present When the operator clicks "Apply" Then extrapolated bays are excluded by default and can be individually included by explicit selection
Autocomplete Seeding from Layout
"As a new store owner, I want smarter autocompletion from day one based on my layout so that arrivals route correctly even before we have historical data."
Description

Initialize arrival and bay prediction logic using the saved layout and door positions so the system produces accurate autocompletes from day one, even with no historical data. Generate geometry-derived features (bay centroids, approach vectors, lot bounds) to guide early detection and prioritize likely bays in the staff UI. Continuously blend live data to refine weights over time. Provide a toggle to disable/enable seeding per location for troubleshooting. Ensure compatibility with pop-up sites that use photo-only layouts and ephemeral numbering.

Acceptance Criteria
Seed Bay Prioritization from Saved Layout
- Given a location with a saved layout (>=1 staging door, >=4 numbered bays) and seeding enabled, when the first arrival is detected with zero historical data, then the Staff UI displays a bay suggestion list ordered by seeded probability within 1 second of detection. - Given two bays at equal centroid distance to the arrival point, when one bay's approach vector aligns within 20° of the arrival bearing and the other's exceeds 45°, then the aligned bay is ranked higher. - Given a layout with N bays, when an arrival is detected, then at least min(3, N) suggestions are shown.
Geometry Feature Generation Completeness
- When a layout is saved, then per-bay centroid, approach vector from the staging door, and lot bounds are computed and persisted as a feature set with a version id and timestamp. - When saving a layout missing a staging door, then seeding feature generation is blocked and a validation error is returned. - When saving a layout with up to 100 bays, then feature generation completes in ≤2 seconds on average measured across 10 runs.
Staff UI Autocomplete Ordering on Day One
- Given zero historical data and seeding enabled, when an arrival occurs, then the top suggestion corresponds to the bay with the minimum great-circle distance between its centroid and the arrival point adjusted by approach-vector weighting. - When staff accepts a suggested bay, then the assignment is recorded within 1 second of tap/click. - When staff selects a different bay than suggested, then the system records the chosen bay and the suggestion list that was shown.
Seeding Toggle Behavior Per Location
- Given seeding is enabled for Location A, when an admin disables it, then subsequent predictions for Location A exclude geometry-derived features and default to non-seeded ordering starting with the next arrival. - When seeding is disabled and then re-enabled, then the previous geometry feature version becomes active without requiring a new layout save. - The seeding toggle state persists across service restarts and is exposed via the location settings API.
Live Data Blending and Weight Updates
- Given seeding is enabled, when a manual bay assignment is recorded, then the live-data store updates within 60 seconds. - Given two arrivals with approach similarity (distance ≤20 m, bearing difference ≤30°) where the first is assigned to Bay X and the seed ranked Bay X below rank 3, when the second arrival occurs within 7 days, then Bay X is ranked at least one position higher than the pure-seed ranking. - For locations with ≥20 labeled arrivals in the past 7 days, then the Top-3 suggestion hit rate over the next 20 arrivals improves by ≥10% compared to the first 20 arrivals with pure seeding in A/B simulation.
Pop-up Photo-only Layout Compatibility
- Given a photo-only layout with drawn bay markers and a staging door, when seeding is enabled, then centroid and approach-vector features are generated from photo coordinates and used for predictions. - When ephemeral numbering is changed and the new layout is published, then prior numbering does not appear in the Staff UI suggestions and predictions reference only the current numbering. - When a photo-only location is archived, then its seed features are deactivated and not used unless a new layout is published.
Prediction Metadata for Troubleshooting
- Each prediction event includes metadata: seeding_enabled flag, feature_version id, top-3 bay ids and scores, and blending_weight values, persisted for ≥30 days. - When seeding is disabled, then prediction metadata reflects seeding_enabled=false and empty geometry feature references. - An admin can export metadata for a given day and location via API with a response time ≤10 seconds for up to 1000 events.
Layout Templates, Versioning & Publish Flow
"As a multi-location manager, I want to version and template layouts so that I can safely update and replicate setups across stores."
Description

Add draft/publish workflow with version history, diffs between versions (bays added/removed/renumbered), rollback, and change logs (who, when, what). Allow saving layouts as templates that can be cloned to new locations and adjusted during onboarding, supporting pop-up events. Gate publishing behind role permissions and require validation to pass before going live. Provide safe publish windows and instant revert. Expose read-only version identifiers to staff UI and arrival services for traceability.

Acceptance Criteria
Role-Gated Publish of a Validated Draft Layout
Given a draft layout passes validation When a user with Publisher or Manager role clicks Publish Then a new Published version with a read-only versionId is created and author/timestamp are recorded Given a user with Editor role attempts to publish When the request is submitted Then the system returns 403 Forbidden with message "Insufficient permissions" Given a draft layout fails validation due to duplicate bay numbers, missing staging door, overlapping geofences, or missing SMS test verification When publish is attempted Then the system blocks publish and returns 422 with a list of failing checks Given publish succeeds When clients poll or receive push updates Then staff UI and arrival services reference the new versionId within 10 seconds of publish
Version History and Diffs Between Layouts
Given a location has multiple versions When viewing version history Then each entry shows versionId, author, timestamp, and label (Published/Rollback) Given two versions are selected for comparison When viewing the diff Then counts of bays added/removed/renumbered and changes to staging door and geofences are displayed and the changed elements are highlighted Given a diff is requested via API When the response is returned Then it includes structured JSON arrays for added, removed, renumbered, and geometry changes with stable identifiers Given two identical versions are compared When viewing the diff Then it displays "No changes" with zero counts
Instant Rollback to a Prior Published Layout
Given a user with Publisher or Manager role and a history of published versions When they select a prior version and click Revert Then a new Published version is created that matches the selected version and is marked as a rollback within 15 seconds Given a rollback completes When clients refresh or receive push updates Then the current versionId is updated and visible in staff UI and arrival services within 10 seconds Given in-flight orders exist at time of rollback When the rollback is executed Then existing order bay assignments remain unchanged and no data loss occurs Given a user without permission attempts rollback When the request is submitted Then the system returns 403 Forbidden Given a rollback occurs When change logs are viewed Then an entry shows who performed it, when, and the source/target versionIds
Create and Clone Layout Templates for Onboarding and Pop-Ups
Given a validated layout When saved as a template with a unique name Then a template is created with metadata (name, tags, createdBy, createdAt) and no location binding Given a template exists When it is cloned to a new location Then a new draft layout is created with bays and numbering copied, staging door placeholder set, and geofences flagged for calibration Given onboarding is in progress When the SMS test drive completes successfully Then geofence validation passes and the draft becomes eligible for publish Given a pop-up event is configured with start and end dates When scheduled publish is enabled Then the cloned layout auto-publishes at start and auto-unpublishes at end, reverting to the prior version Given a template is modified or deleted When checking previously cloned layouts Then those cloned layouts remain unchanged
Comprehensive Change Log and Audit Trail
Given any layout action (create, edit, publish, rollback, schedule, cancel) occurs When the action completes Then a change log entry is recorded with userId, role, UTC timestamp, action, summary, and a link to the diff/entity Given change log entries exist When a user requests to edit or delete an entry Then the system returns 405 Method Not Allowed and the entry remains unchanged Given a user with appropriate access When filtering the change log Then results can be constrained by location, user, action, and date range and exported as CSV via API Given an unauthorized user requests the change log When access control is enforced Then the system returns 403 Forbidden
Safe Publish Windows and Scheduling
Given safe publish windows are configured for a location When a user attempts to publish outside a window Then the system blocks with 409 Conflict and offers to schedule for the next window Given a publish is scheduled When viewing scheduled jobs Then the job shows next execution time, requester, and status (Queued/Running/Completed/Failed) Given a scheduled publish executes When it completes successfully Then a new Published version is created with audit trail and notifications sent to subscribers Given a user with Admin role provides an override reason When publishing outside a window Then the publish proceeds immediately and the override is logged Given two publishes are scheduled for overlapping times When the second is created Then it is queued behind the first and the user is notified of the new ETA
Expose Read-Only Version Identifiers for Traceability
Given staff UI is open When a layout is published or rolled back Then the header displays the current read-only layout versionId and last updated within 10 seconds Given arrival API responses are returned When a customer checks in Then each response includes layoutVersionId corresponding to the evaluation used Given audit logs are queried When viewing arrival events Then each event includes the layoutVersionId in effect at evaluation time Given a client attempts to set or modify versionId via API When the request is processed Then the server ignores the change, logs the attempt, and returns 400 or omits the field

Proximity Priority

Weights assignments for guests who need closer access or during harsh weather, preferring near‑door, sheltered, or wider bays when available. Preserves dignity for mobility‑limited guests and keeps the flow smooth without special handling calls.

Requirements

Bay Metadata Registry
"As an operator, I want to record and manage each bay’s attributes so that assignments can consider proximity, shelter, and accessibility automatically."
Description

Define and persist structured attributes for each curbside bay (e.g., distance to door, width/clearance, shelter/awning presence, lighting, signage visibility, ADA-reserved status, snow coverage risk) and associate them with locations/shifts. Provide an admin UI or CSV import to maintain bay inventory and availability windows. Expose these attributes via a fast read API to the assignment engine so it can evaluate proximity, shelter, and width needs in real time. Supports multi-lot and dynamic closures (e.g., cones, blocked bays) with effective dating and quick toggles.

Acceptance Criteria
Schema Definition and Persistence for Bay Attributes
Given a new bay is defined for a location When the bay record is saved Then the system persists structured attributes: bay_id (string, unique per location), lot_id (optional string), distance_to_door_m (number ≥ 0), width_m (number ≥ 0), shelter (boolean), lighting (enum: none|low|medium|high), signage_visibility (enum: low|medium|high), ada_reserved (boolean), snow_risk (enum: low|medium|high), effective_start (optional ISO8601), effective_end (optional ISO8601), notes (string ≤ 255) And subsequent reads of the bay return exactly the saved values And attempting to save missing required fields (bay_id, distance_to_door_m, width_m) fails with field-level errors And attempting to create a duplicate bay_id within the same location is rejected with a 409/validation error
Admin UI Bay CRUD and Field Validation
Given an authenticated admin with access to a location When they create, edit, or delete a bay via the UI Then form inputs enforce data types and allowed values on client and server And invalid entries display inline error messages without full page reload And successful save updates updated_at and shows a success notification And delete performs a soft delete that removes the bay from the read API and marks deleted_at And concurrent edits are protected with optimistic locking; stale updates are rejected with a conflict prompt
CSV Import with Preview, Idempotency, and Error Reporting
Given an admin uploads a CSV with columns: bay_id, lot_id, distance_to_door_m, width_m, shelter, lighting, signage_visibility, ada_reserved, snow_risk, effective_start, effective_end, notes When they request a dry-run Then the system validates all rows and returns a preview summary showing will_create, will_update, will_error counts and sample error rows with line numbers and messages When they confirm import Then rows are upserted idempotently by (location_id, bay_id); duplicates in the file apply last occurrence And the import processes 2,000 rows in ≤ 10 seconds p95 And a downloadable error report (CSV) lists failed rows with reasons; successful rows are committed atomically per batch
Availability Windows and Shift Association
Given a bay has availability windows and is associated to one or more shifts for a location When the current time in the location’s timezone falls within an open window and the corresponding shift is active Then the bay is considered available And when a closure window overlaps an open window Then the closure takes precedence and the bay is unavailable during the overlap And when no windows are defined Then the bay defaults to available And the read API reflects the same availability determination
Dynamic Closures and Quick Toggle
Given a manager toggles a bay to Closed Now with a reason code When they confirm the toggle Then the bay becomes unavailable immediately and is excluded from the read API within 2 seconds And the closure records operator_id, reason, start_timestamp, and optional until_timestamp And when until_timestamp elapses Then the bay auto-reopens and becomes available in the read API within 2 seconds And reopening manually clears the closure and records operator_id and timestamp
Fast Read API for Assignment Engine
Given the assignment engine requests bays for a location When calling GET /v1/bays?location_id={id}&filters (lot_id, ada_reserved, shelter) Then the API returns only currently available bays with all attributes needed for proximity and accessibility evaluation, including distance_to_door_m and width_m And closed or out-of-window bays are excluded And the response p95 latency is ≤ 100 ms for up to 200 bays and p99 ≤ 200 ms And the response includes caching headers (ETag and Last-Modified) and a stable schema version
Multi-Lot Support and Scoping
Given a location has multiple lots with bays assigned to lots When fetching bays by lot_id Then only bays in that lot are returned And when fetching by location_id Then bays from all lots are returned, each including lot_id And the system enforces that a bay cannot be assigned to multiple lots simultaneously at save-time
Weather-aware Context Engine
"As a shift lead, I want the system to automatically recognize harsh weather so that guests are prioritized for nearer or sheltered bays without me micromanaging assignments."
Description

Integrate a reliable weather service to infer harsh conditions (e.g., heavy rain/snow, high wind, extreme heat/cold) based on store geolocation and current/near-nowcast data. Publish a normalized context signal (e.g., NORMAL, INCLEMENT, SEVERE) with confidence and TTL to the assignment algorithm. Handle API rate limits, caching, fallback defaults, and store-timezone alignment. Allow per-store thresholds and blackout windows (e.g., covered drive under construction) via configuration.

Acceptance Criteria
Publish Normalized Context Signal with Confidence and TTL
Given the context engine evaluates weather for a store When it publishes the context signal Then the payload includes fields: state ∈ {NORMAL, INCLEMENT, SEVERE}, confidence ∈ [0.0, 1.0], ttlSeconds > 0, asOf (ISO-8601 with store timezone offset), and storeId And the assignment algorithm receives the signal on its input channel And only one state is present per publication
Map Weather Data to Context Using Store Geolocation and Nowcast
Given a store geolocation and current/near-nowcast weather data When observed or imminent conditions meet the configured thresholds Then the engine maps to INCLEMENT or SEVERE accordingly; otherwise NORMAL And the computed confidence meets or exceeds the configured minimum for the selected state And the state updates on the next evaluation cycle after the threshold crossing
Per‑Store Threshold Overrides
Given store-specific threshold configuration is defined When identical external weather conditions are evaluated for a default store and the configured store Then the resulting states reflect their respective thresholds (potentially different) And when the store-specific overrides are removed Then the mapping reverts to the global defaults without requiring a restart
Caching Behavior for Weather Fetches
Given a configurable cache TTL for raw weather lookups per store When multiple evaluations occur within the cache TTL window Then no additional external weather API calls are made for that store within the window And the published context uses the cached snapshot And after the cache TTL expires, the next evaluation triggers exactly one new fetch
Rate Limit Handling and Backoff
Given the weather provider responds with HTTP 429 and a Retry-After value When the engine receives the 429 Then it stops retrying that provider for the indicated backoff period And it continues publishing context using the latest cached snapshot until backoff ends And it resumes normal polling after the backoff without dropping messages
Fallback Defaults on Provider Failure or Expired TTL
Given the external weather API is unavailable or times out and the last context TTL has expired When the engine attempts to evaluate the current state Then it publishes the configured per-store fallback state with the configured fallback confidence and TTL And it logs/metrics the fallback event once per TTL window And it automatically returns to live weather-derived states when the API recovers
Store Timezone Alignment and Blackout Windows
Given a store with a defined timezone and blackout windows When the current time falls within the blackout window in the store’s local timezone Then the engine does not escalate above NORMAL regardless of external conditions And outside the blackout window, normal mapping rules apply And asOf and TTL calculations reflect the store’s local timezone alignment
Accessibility Preference Capture (SMS-first)
"As a guest with mobility limitations, I want an easy, private way to indicate I need a near or wider bay so that I’m assigned a spot I can safely access without calling the store."
Description

Collect guest proximity/access needs via the existing SMS/web ‘I’m here’ flow using neutral, dignity-preserving prompts (e.g., “Prefer near entrance or extra space today?”). Support one-time and remembered preferences (with consent), as well as temporary flags (e.g., recovering from injury). Do not expose sensitive labels to other guests or in public-facing messages. Store only what’s necessary, with short retention and easy opt-out. Include support for ADA placard confirmation if assigning ADA-reserved bays is enabled by the operator.

Acceptance Criteria
SMS 'I'm here' prompt captures neutral proximity preference
Given a guest clicks the 'I'm here' link from the order SMS When the web check-in page loads Then the page displays an optional, single-question prompt with neutral wording: "Prefer near entrance or extra space today?" And the available choices are "Near entrance", "Extra space/wider bay", and "No preference" And the guest can proceed without selecting an option And no medical or disability labels are displayed anywhere on the page And the selected option (if any) is stored for the current visit only unless explicit consent to remember is given
Remember and apply preferences with explicit consent
Given a guest selects a proximity preference during check-in And a consent control labeled "Remember for future visits" is shown and defaults to Off When the guest enables the consent control and submits Then the system stores only the selected preference, consent flag, and consent timestamp keyed to the guest phone number And on the guest's next check-in within the retention window, the preference is pre-filled and applied by default And if the guest leaves consent Off, no preference is stored after the visit completes And stored preferences automatically expire after the operator-configured retention window (default 30 days, maximum 90 days)
Temporary accessibility needs with time-bound flag
Given a guest indicates they have a temporary proximity/space need When the system asks for duration Then the guest can choose "Today only" or a time-bound period up to 90 days And the temporary flag is applied only during the selected period And the flag auto-expires at the end of the period and is deleted And the guest can clear the flag anytime from the check-in page before expiry
ADA placard confirmation for ADA-reserved bays
Given the operator has ADA-reserved bays enabled And a guest's selection would require assignment to an ADA-reserved bay When assigning a bay Then the guest is prompted to confirm possession of a valid ADA placard with a Yes/No choice And if Yes, an ADA-reserved bay may be assigned when available And if No or no response, assign a non-ADA near-door or wider bay when available And the ADA confirmation applies only to the current visit unless the guest explicitly opts to remember it And if ADA-reserved bays are not enabled, no ADA confirmation prompt is shown
Privacy: no sensitive labels in public or guest-facing messages
Given a guest preference or ADA confirmation has been captured When sending any guest-facing SMS or showing any public-facing screen Then only neutral operational phrasing is used (e.g., "Bay 2 near entrance") And no terms such as "ADA", "disabled", "handicap", or any health descriptors appear And staff tools show only neutral indicators ("Prefer near entrance", "Extra space") with no medical labels And logs/analytics store only preference codes, timestamps, and retention metadata—no free-text or medical descriptors
Opt-out and data retention limits for stored preferences
Given a guest has previously consented to store a preference When the guest replies "FORGET" to the service SMS or taps "Forget my preferences" Then the system deletes all stored preference data for that phone within 5 minutes And sends a confirmation SMS of deletion And future check-ins are not pre-filled with any preference And all stored preferences auto-delete at the end of the retention window And operators can configure the retention window between 30 and 90 days
SMS-only capture fallback for check-in
Given a guest replies to the 'I'm here' SMS without opening the web link When the system initiates SMS-based check-in Then it sends a neutral preference question with numbered options: 1 Near entrance, 2 Extra space, 3 No preference And the guest can reply with 1/2/3 to select And the flow proceeds without requiring a response if the guest replies 3 (No preference) And consent to remember is requested via a single Y/N SMS if a preference (1 or 2) was selected And the total additional SMS for this flow does not exceed 3 per guest unless ADA confirmation is required
Proximity-weighted Assignment Algorithm
"As an operator, I want the system to automatically allocate the most suitable bay given conditions and needs so that service stays fast and respectful without manual intervention."
Description

Enhance the assignment engine to score and select bays using guest preferences, weather context, and bay attributes while maintaining throughput and fairness. Implement a configurable scoring model with hard constraints (e.g., never assign ADA-reserved bay without eligibility) and soft weights (e.g., prefer shelter during rain, prefer near-door for proximity needs). Handle real-time state changes (bay occupancy, no-shows, reassignments) and degrade gracefully when no ideal bay exists. Provide deterministic tie-breaking and logging for auditability.

Acceptance Criteria
ADA-Reserved Bay Hard Constraint Enforcement
Given a non-ADA-eligible guest, When the assignment engine scores bays, Then ADA-reserved bays are excluded from eligibility and cannot be assigned. Given an ADA-eligible guest and at least one free ADA-reserved bay, When scoring, Then ADA-reserved bays are eligible and may be selected if they have the highest total score. Given any assignment run, When hard constraints are applied, Then the decision log records excluded bay IDs with reason "ADA_RESERVED_NOT_ELIGIBLE". Given a non-ADA-eligible guest and no free non-ADA bays, When assigning, Then the guest is placed in "Waiting for bay" status and no ADA-reserved bay is assigned.
Weather-Based Shelter Preference
Given current weather condition is Rain (intensity >= Light) at assignment time, When scoring, Then sheltered bays receive a positive weight per configuration and their component score is included in logs. Given at least one eligible sheltered bay is free, When choosing between a sheltered and unsheltered bay with otherwise equal attributes, Then the sheltered bay is assigned. Given no eligible sheltered bay is free, When assigning during rain, Then the engine assigns the highest-scoring eligible bay without delaying the order due to shelter preference.
Proximity-Need Guest Near-Door/Wide Bay Preference
Given a guest has proximity_need=true, When scoring, Then near-door and wider bays receive configured positive weights for this guest only. Given proximity_need=true and at least one eligible near-door or wider bay is free, When assigning, Then the highest-scoring eligible bay with these attributes is selected. Given proximity_need=true and no eligible near-door or wider bay is free, When assigning, Then the system assigns the best available eligible bay without delay and records "NO_PREFERRED_ATTRIBUTES_AVAILABLE" in the log. Given proximity_need=false, When scoring, Then proximity-related weights are not applied.
Real-Time Occupancy Changes, No-Shows, and Reassignment
Given a guest is assigned to Bay X and Bay X becomes occupied before the guest arrives, When the occupancy change event is received, Then the system re-scores and reassigns to the next highest-scoring eligible bay within 2 seconds and notifies staff and the guest. Given no eligible bay is available at reassignment time, When reassignment is triggered, Then the guest status becomes "Waiting for bay" and the engine retries scoring on each bay state change or every 10 seconds, whichever occurs first. Given a guest is a no-show for N minutes (configurable, default 7) after assignment, When the timer elapses, Then the bay is released, the guest is marked "No-show", and the decision is logged with reason "NO_SHOW_TIMEOUT".
Queue Fairness and Deterministic Selection Under Ties
Given two guests compete for the same preferred bay with equal priority and arrival within the same minute, When selecting which guest is assigned first, Then the guest with the earlier check-in timestamp is assigned first; if identical to the millisecond, the lower order ID is chosen. Given two or more eligible bays have identical total scores for a single guest, When selecting among them, Then the engine deterministically chooses the bay with the lowest numeric bay_id. Given any tie is resolved, When logging, Then the log records the tie context, candidates, tie-breaker rule(s) applied, and the selected outcome.
Configurable Scoring Model and Safe Validation
Given an authorized user updates scoring weights within allowed bounds, When the configuration is saved, Then new assignments reflect the updated weights within 5 seconds without service restart. Given a configuration contains an invalid value (non-numeric or outside bounds), When saving, Then the change is rejected, prior configuration remains active, and a validation error is returned and logged with details. Given service startup, When the scoring model initializes, Then default weights and constraint rules are loaded and logged with version and checksum. Given a configuration change is applied, When auditing, Then an entry records actor, timestamp, fields changed, old/new values, and optional reason.
Decision Auditability and Secure Logging
Given any bay assignment or reassignment decision, When it is made, Then a structured log entry contains: order ID, guest flags, weather snapshot (source, timestamp, condition), evaluated bay IDs with component scores, applied hard constraints, total scores, selected bay ID, tie-breaker (if any), and timestamps. Given a query by order ID within 30 days, When retrieving logs, Then the decision record is returned within 2 seconds and PII (e.g., phone numbers) is masked except the last 4 digits. Given an assignment attempt fails due to no eligible bays, When logging, Then the entry includes reason codes and the next retry schedule.
Staff UI Rationale & Override
"As a curbside runner, I want to see why a bay was chosen and quickly override when the lot changes so that I can keep the handoff smooth and accurate."
Description

Update the staff console to display the chosen bay with a concise, neutral rationale (e.g., “Closest available” or “Sheltered due to weather”), without revealing sensitive guest details. Provide one-tap override with suggested alternatives and require a reason code to improve future tuning. Include visual indicators for bay status, ADA-reserved designation, and conflicts. Ensure mobile-friendly performance and real-time sync with the assignment engine.

Acceptance Criteria
Rationale Display Without Sensitive Details
Given a staff user views an arrival assignment, When the bay card is rendered, Then a rationale label is shown using one of the approved phrases: "Closest available", "Sheltered due to weather", "Near-door for accessibility", "Wider bay needed", or "Load balancing". Given any rationale text is displayed, When scanning its content, Then it contains no guest identifiers, medical information, disability status, phone numbers, or free-text content. Given harsh weather is active, When the chosen bay is sheltered, Then the rationale reads "Sheltered due to weather". Given the assignment engine prioritized accessibility, When the rationale is shown, Then it reads "Near-door for accessibility". Given the rationale phrase is rendered, Then its length is 60 characters or fewer. Given an assignment is displayed, When recorded in the audit log, Then the rationale phrase, bay ID, order ID, and timestamp are persisted.
One-Tap Override With Reason Code
Given the assignment card is visible, When a staff user taps "Override", Then the alternatives drawer opens within 300 ms. Given the alternatives drawer is open, When the user taps any listed bay, Then that bay becomes the pending selection and the "Confirm" button remains disabled until a reason code is chosen. Given no reason code is selected, When the user attempts to confirm, Then the UI blocks confirmation and surfaces an inline error "Select a reason". Given a reason code is selected from a controlled list ["Customer request", "Bay blocked", "ADA bay required", "Weather shelter", "Operational load balancing"], When the user taps "Confirm", Then the override is submitted and persisted with actor, from-bay, to-bay, reason, and timestamp. Given the override fails to save, When the API returns an error, Then the UI restores the original bay and displays an error message.
Suggested Alternative Bays Ranking
Given the alternatives drawer is open, When suggestions are displayed, Then up to three bays are shown ranked by proximity, shelter, and width suitability for the current order context. Given a bay is occupied, blocked, under maintenance, or reserved for ADA and not applicable, When compiling suggestions, Then that bay is excluded from the suggestions list. Given there are no suitable alternatives, When the drawer opens, Then show "No suitable alternatives" and disable the Confirm action. Given bay statuses change in real time, When a suggested bay becomes unavailable, Then it is removed from the list within 1 second.
Bay Status, ADA, and Shelter Indicators
Given the staff console bay list is displayed, When viewing any bay card, Then the following indicators are visible: status (Open/Occupied/Reserved/Blocked), ADA icon for ADA-reserved bays, and a shelter icon for covered bays. Given a bay is ADA-reserved, When displayed, Then the ADA icon uses a distinct color and has accessible name "ADA-reserved". Given a bay is in conflict (double assignment) or blocked, When displayed, Then a conflict badge is shown and the card is not auto-assignable. Given keyboard or screen reader navigation, When focusing indicators, Then their accessible names are announced and can be toggled without pointer input.
Conflict Detection and Guardrails
Given a bay has an active assignment within the next 2 minutes, When a second assignment would target the same bay, Then the UI surfaces a conflict banner and requires override with reason to proceed. Given a non-priority order attempts to occupy an ADA-reserved bay, When confirming, Then the UI blocks confirmation and requires either a qualifying reason code or selection of a non-ADA bay. Given a bay is marked blocked or out of service, When selected, Then the UI prevents confirmation and suggests alternative bays. Given an assignment changes while the override drawer is open, When the conflict affects the current selection, Then the drawer refreshes and the user is notified of the change.
Mobile-Friendly Performance Targets
Given a mid-tier mobile device on 4G, When loading the staff console bay view, Then first contentful paint occurs within 1.5 seconds and the bay list is interactive within 2.0 seconds. Given user taps Override, When the drawer opens, Then tap-to-interactive latency is under 100 ms and all tap targets are at least 44x44 px. Given viewport widths 360–414 px, When rendering, Then the layout does not overflow horizontally and critical actions remain visible without scrolling. Given the console is idle, When 60 seconds elapse, Then the app consumes less than 1% CPU on average and no memory leak exceeding 10 MB is observed.
Real-Time Sync With Assignment Engine
Given the assignment engine updates a bay assignment, When the staff console is open, Then the change is reflected within 300 ms via WebSocket or within 2 seconds via fallback polling. Given a staff user confirms an override, When the engine acknowledges, Then the new assignment appears across all staff consoles within 1 second and the old assignment is cleared. Given the engine rejects an override due to a race condition, When the response arrives, Then the UI shows an error and refreshes suggestions to the latest state. Given network loss occurs, When connectivity resumes, Then the console resynchronizes assignments within 5 seconds and flags any unresolved conflicts.
Privacy & ADA Compliance Safeguards
"As a compliance-conscious operator, I want built-in safeguards around accessibility data and ADA-reserved spots so that we respect guests and meet legal obligations."
Description

Enforce privacy-by-design: minimize data collection, apply short retention for sensitive flags, and mask guest-specific needs in staff-facing and SMS outputs. Gate use of ADA-reserved bays behind explicit store enablement and guest self-attestation, with audit logs. Provide configuration to exclude certain bays from consideration, and ensure accessible bays are never auto-assigned to ineligible guests. Include DSR/opt-out flows and clear consent copy in the SMS/web experience.

Acceptance Criteria
Data Minimization in Arrival Workflow
Given a guest follows the SMS “I’m here” link When the arrival is registered Then the system stores only: orderId, storeId, maskedPhone (last 4), arrivalTimestamp, bayId (if assigned), and a boolean priorityAttested And no GPS coordinates, device identifiers, medical details, or vehicle VIN/plate are collected or persisted And analytics events contain only non-PII (storeId, order timings, bayId) with phone irreversibly hashed-and-salted And staff UI exposes no field indicating disability, health, or attestation text
Masking of Guest Needs in Staff and SMS Outputs
Given a guest has self-attested priority When staff view the queue or assignment panel Then the UI displays a neutral “Priority” label/icon without terms like ADA, disability, mobility, or wheelchair And bay labels shown to staff omit reasons (e.g., “Bay 2 — Wide” not “ADA”) And when the guest receives SMS confirmations Then no message text reveals attestation or reason And server logs and exports mask the priority flag to a generic “priority=true” without attestation text
ADA Bay Assignment Eligibility Gate
Given the store has not enabled ADA-reserved bays When the assignment engine runs Then bays marked accessible are excluded from auto-assignment for all guests Given the store has enabled ADA-reserved bays and defined at least one accessible bay When a guest self-attests priority in-session Then only that guest becomes eligible for accessible bays And non-attested guests are never auto-assigned an accessible bay And when any accessible bay assignment occurs Then an audit log entry records storeId, orderId, bayId, attestationTimestamp, eligibility=true, and assignmentDecisionId
Sensitive Flag Short Retention and Automated Purge
Given a guest self-attests priority When the order is completed or canceled Then the priorityAttested flag and related metadata are deleted within 24 hours And backups and analytics stores receive a deletion job within 7 days And audit logs retain only a non-sensitive proof (attestation presence hash) for a maximum of 90 days And when retention windows elapse Then verification via data export shows the flag is absent
Excluded Bays Omitted From Assignment
Given a manager with role “Store Admin” marks Bay X as Excluded in settings When the assignment engine runs Then Bay X is never returned in candidate lists or UI suggestions And when the manager removes the exclusion Then Bay X becomes eligible on the next assignment cycle (<60 seconds) And all changes (userId, bayId, oldValue, newValue, timestamp) are captured in audit logs And when no eligible non-accessible bays remain for an ineligible guest Then the system queues the guest without assignment and displays “Awaiting bay” to staff
Explicit Consent Prior to Sensitive Flag Collection
Given the flow requests self-attestation or device geolocation When the consent screen is shown Then the copy states purpose, retention, and that service is available without attesting And the consent choice defaults to No and requires explicit Yes And when consent is denied Then no sensitive flag or geolocation is collected and the flow proceeds with standard assignment And consent decisions store timestamp, locale, and versioned policyId and are retrievable in audit
Guest DSR: Access, Delete, and Opt-Out via SMS/Web
Given the guest replies “DATA” via SMS or taps “Privacy options” on the web link When they verify via a one-time code sent to their phone Then a page lists their stored data (contact, open orders, last 30 days of arrivals) within 60 seconds And when the guest requests deletion Then personal data is deleted within 7 days and a confirmation SMS is sent And when the guest opts out Then future sessions suppress sensitive prompts and analytics linking while core pickup still functions And each DSR action is written to an immutable audit log with action type, timestamp, requester, and outcome
Performance & Impact Analytics
"As a product owner, I want visibility into how proximity-weighted assignments affect speed and guest satisfaction so that we can tune weights and prove ROI."
Description

Instrument key metrics to assess Proximity Priority effectiveness: assignment match rate to guest needs, wait time deltas in harsh weather, utilization of sheltered/wider bays, reassignments, overrides, and ADA bay compliance. Provide dashboards and export to CSV, with filters by store, shift, weather condition, and time range. Support A/B or feature flag comparisons to validate improvements without degrading throughput.

Acceptance Criteria
Assignment Match Rate by Store and Shift
Given Proximity Priority analytics are enabled and guest need flags are captured on orders When I filter the dashboard to a specific Store, Shift, Weather = All, and a defined Time Range Then I see Assignment Match Rate (%) with numerator = count of orders where assigned bay matched guest need and denominator = count of orders with a declared need And missing/unknown need data is excluded from the denominator And the displayed percentage and counts match the CSV export produced with the same filters (±1 order) And applying different shifts or stores updates the metric accordingly without altering unfiltered totals
Harsh Weather Wait Time Delta Reporting
Given orders are tagged with weather conditions including a Harsh category (e.g., rain, snow, extreme heat/cold) When I enable Baseline Comparison = Clear weather and filter to a Store and Time Range Then the dashboard shows median and p90 Wait Time for Harsh and Baseline cohorts and Delta = Harsh − Baseline (minutes, 1 decimal) And sample sizes (n) for both cohorts are displayed And the cohort medians and deltas match the CSV export for the same filters within ±0.1 minutes
Sheltered/Wider Bay Utilization Metrics
Given bays have attributes configured for Sheltered and Width (e.g., Wider) When I view Utilization with filters Store, Shift, Weather = All/Harsh, and Time Range Then I see per-attribute utilization including: - Percent of assignments to bays with the attribute - Average occupancy minutes per assignment for bays with the attribute And totals across bay attributes do not exceed 100% of assignments And counts and averages match the CSV export under the same filters (±1 assignment; ±0.1 minutes)
Reassignments and Staff Overrides Tracking
Given the system logs bay reassignments and staff overrides of Proximity Priority suggestions When I filter to a Store and Time Range Then I see total counts and rates per 100 orders for Reassignments and for Overrides And I can drill down to an event table with columns: timestamp, order_id, original_bay, new_bay, actor, action_type, reason(optional) And exporting CSV produces one row per event with those columns And dashboard counts equal the number of rows in the exported CSV for the same filters (±1 event)
ADA Bay Compliance Metrics
Given certain bays are flagged ADA-only and guests may be flagged ADA-needed When I open the ADA Compliance widget with Store and Time Range filters Then I see: - Percent of ADA-needed guests assigned to ADA-eligible bays - Percent of non-ADA guests assigned to ADA-only bays - Count of conflicts where a non-ADA guest occupied an ADA-only bay while ADA-needed guests were waiting And metrics are available per store and aggregated across selected stores And values match the CSV export under the same filters within ±1 event
Filtered CSV Export Schema and Fidelity
Given filters Store, Shift, Weather condition, and Time Range are applied When I click Export CSV Then a CSV downloads within 10 seconds containing only rows that match the active filters And the CSV includes columns: order_id, store_id, shift, order_time_utc, weather_condition, guest_need_flags, assigned_bay_id, bay_attributes, wait_time_minutes, reassigned_flag, override_flag, cohort_flag (A/B), is_ada_bay And row counts and aggregated metrics in the CSV equal the dashboard widgets for the same filters (±1 row; ±0.1 minutes)
A/B Feature Flag Comparison View
Given an experiment or feature flag is active with cohorts Control (Off) and Treatment (On) When I select Comparison = A/B and apply Store and Time Range filters Then the dashboard displays for each cohort: orders, assignment match rate, median wait time, p90 wait time, reassignments per 100 orders, overrides per 100 orders, throughput (orders/hour) And the view shows absolute and percent deltas (Treatment vs Control) for each metric And the same filters apply to both cohorts And cohort values match CSV exports filtered by cohort within ±1 order or ±0.1 minutes as applicable

Smart Role Match

Automatically assigns the correct, least‑privilege role at login by reading the staffer’s schedule, station/device context (Expo tablet vs. Curb Captain board), and location rules. Cuts permission mistakes, speeds clock‑in, and puts each teammate on the right screen from second one.

Requirements

Least-Privilege Role Resolver
"As a clocking-in staffer, I want my role to be automatically resolved with only the permissions I need so that I land on the right screen immediately and avoid mistakes."
Description

Implements the core decision engine that determines a staffer’s effective role at login by evaluating schedule data, station/device tags, and location rules. Applies least-privilege principles, tie-breakers, and time windows (early/late grace) to resolve a single active role or approved multi-role set. Outputs permission scopes and target UI route, ensuring teammates start on the correct screen, reducing access errors, and minimizing manual role selection.

Acceptance Criteria
Assign scheduled role on correct station within grace window
Given a staffer has an active shift for role "Expo" at location L from 10:00 to 16:00 with early_grace 10m and late_grace 10m And the login originates from a device tagged station "Expo" at location L And the current time is between 09:50 and 16:10 When the staffer authenticates successfully Then the resolver selects effectiveRoles ["Expo"] and no other roles And the permission scopes exactly match the "Expo" role scope set And the targetRoute equals the "Expo" default route And the manual role picker is not shown And the decision is computed within 250 ms And a decision log entry with reasonCodes ["match:schedule","match:station","within:grace"] is recorded
Resolve schedule–station conflicts via location rule precedence
Given the staffer is scheduled "Runner" 12:00–18:00 at location L And the device station tag is "Curb Captain" at location L And location rule precedence is device_over_schedule When the staffer logs in between 12:00 and 18:00 Then the resolver selects ["Curb Captain"] And reasonCodes include ["precedence:device_over_schedule","conflict:schedule_vs_station"] And manual role picker is not shown And the targetRoute equals the "Curb Captain" default And the decision log captures evaluated inputs and applied rule id
Approve multi-role set for overlapping duties
Given the staffer has overlapping shifts "Curb Captain" and "Runner" 17:00–19:00 And location rules permit the multiRoleSet ["Curb Captain","Runner"] with scopeSet "CC_Runner_Union" When the staffer logs in on a neutral device at 17:15 Then effectiveRoles equals ["Curb Captain","Runner"] in defined priority order And permission scopes equal the configured scopeSet "CC_Runner_Union" (no additional scopes) And targetRoute equals the configured multi-role default route And an in-app role switcher is enabled without re-authentication And audit log includes multiRoleSetId and scopeSetId
Apply early/late grace windows and fallback outside windows
Rule 1: If login time is within early_grace before shift start or within late_grace after shift end on a matching station, assign the scheduled role and normal scopes. Rule 2: If login time is outside both grace windows, assign fallback role "Clock-In Only" with restricted scopes and route "/timeclock". Rule 3: The resolver must not allow access to operational routes when fallback role is assigned. Rule 4: All decisions emit reasonCodes indicating "within:early_grace", "within:late_grace", or "outside:grace" accordingly.
Safe fallback on missing schedule or station context
Given schedule data is unavailable (timeout > 2s) or device station tag is missing/invalid When the staffer logs in Then the resolver assigns fallback role "Clock-In Only" And permission scopes are limited to timekeeping and profile only And targetRoute is "/timeclock" And a warning event is logged with errorType and retryAfter And no scopes beyond the fallback role are granted
Emit structured decision payload and audit event
Given any successful role resolution When the resolver returns a decision Then the client receives a payload containing userId, locationId, effectiveRoles, scopeSetId, targetRoute, reasonCodes, decisionAt, precedenceRuleId, deviceStationTag, scheduleWindow, graceApplied And the payload conforms to schemaVersion "roleDecision.v1" and passes validation And an audit event with the same correlationId is published within 200 ms of login And no sensitive PII fields are present in the payload
Schedule Sync & Identity Mapping
"As an operations manager, I want CurbPing to read our shift assignments and roles so that teammates get the correct access when they clock in."
Description

Aggregates shift and role assignments from scheduling sources (e.g., CSV/iCal upload and supported APIs) and maps them to CurbPing identities (SMS phone numbers or SSO accounts). Supports real-time updates via webhooks or periodic polling, grace periods for early/late arrivals, multi-store calendars, and caching for performance. Ensures Smart Role Match has accurate, timely shift-role context to drive correct permissioning at login.

Acceptance Criteria
Webhook-Driven Real-Time Shift Update
Given a supported scheduling source sends a shift create/update/delete webhook for staffer X with role R at store S When the webhook is received by CurbPing Then the schedule cache and identity-role mapping for X are updated within 10 seconds and available for the next login at store S And Then if X logs in after the update at store S, Smart Role Match provides role R And Then duplicate delivery of the same webhook (same event ID) is handled idempotently with no duplicate shifts created And Then transient failures are retried up to 3 times with exponential backoff; permanent failures are logged with error code and surfaced to monitoring
Polling Fallback for Non-Webhook Sources
Given a scheduling source is configured for polling at interval I (I ≤ 5 minutes) When a shift change occurs in the source Then CurbPing ingests the change and updates identity-role mapping within I + 60 seconds And Then if the source is unreachable, a warning is logged and the last known schedule is used for up to 60 minutes before an alert is raised And Then once connectivity is restored, missed changes from the last 24 hours are reconciled without duplication
CSV and iCal Upload Ingestion
Given an authorized manager uploads a valid CSV or iCal file containing shifts for store S When the upload is processed Then 100% of valid rows/events are ingested and mapped to CurbPing identities using external staff ID, SSO ID, or E.164-normalized phone number And Then duplicate rows/events (same external ID + start time + store) are ignored and summarized as duplicates And Then file time zones are honored; if absent, the store S time zone is applied And Then a processing report is produced with counts of ingested, duplicates, and failures; each failure includes row/event reference and reason And Then if >5% of rows/events fail validation, the upload is rejected unless the manager explicitly confirms override
Identity Mapping: Phone, SSO, and Conflict Handling
Given a schedule entry includes staff external ID E and/or phone P and/or SSO unique ID U When mapping to a CurbPing identity Then a single identity is selected by exact match on U, else on E, else on normalized phone P (E.164), in that order of precedence And Then if multiple identities share the same E, U, or P, the mapping fails closed, a conflict record is created, and the staffer is not auto-assigned a role And Then if an identity has both SSO and SMS credentials, they resolve to the same canonical user and yield the same role at login And Then all mappings and conflicts are auditable with timestamp, source, and identifiers used
Grace Periods for Early/Late Logins
Given store S has configured early grace G_early and late grace G_late When a staffer logs in between shift_start − G_early and shift_start + G_late Then Smart Role Match receives the scheduled role for that shift And Then if login occurs outside the grace window, the configured fallback policy is applied (deny or default role), and the reason is logged And Then changes to grace period settings take effect within 60 seconds of save
Multi-Store Calendars and Store-Scoped Mapping
Given staffer X has shifts in multiple stores S1 and S2 that overlap When X logs in from a device associated with S1 Then the S1 shift-role is selected, and S2 shifts are ignored for that session And Then if no device-store association is present, the schedule is filtered by the store selected at login; if none is selected, the user is prompted to choose a store once And Then overlapping shifts across stores (>15 minutes overlap) are flagged for manager review without blocking login
Caching and Performance SLA
Given the schedule cache is warm When Smart Role Match queries shift-role data at login Then the lookup completes with p95 ≤ 200 ms and p99 ≤ 500 ms over the last 24 hours And Then cache TTL is configurable (default 60 seconds) and is selectively invalidated upon webhook updates for affected staffers only And Then cache misses trigger a single read to the backing store with request coalescing to prevent thundering herd under concurrent logins
Station/Device Context Detection
"As an expo lead logging into the expo tablet, I want the system to recognize the station so that it opens the expo workflow without extra steps."
Description

Provides robust station awareness by tagging devices as Expo Tablet, Curb Captain Board, or Front-of-House upon provisioning via secure QR/deep link. Persists station identity locally, supports verified re-tagging, and performs lightweight device attestation. Falls back to an untagged state with restricted permissions if context is uncertain. Aligns the login experience with the physical station to reduce navigation and errors.

Acceptance Criteria
Secure QR/Deep Link Provisioning Tags Device to Station
Given an untagged device and a valid one-time provisioning QR/deep link issued by Org A for station type "Expo Tablet" When the link is opened within 15 minutes of issuance and the device confirms org and station Then the device is tagged locally as "Expo Tablet" for Org A and least-privilege capabilities for Expo are enabled And the provisioning token is invalidated within 1 second and cannot be reused And attempts with an expired/reused token are rejected with HTTP 400 and message "Provisioning link invalid or expired" And station type must be one of ["Expo Tablet","Curb Captain Board","Front-of-House"]; any other value is rejected with HTTP 400
Persistent Station Identity Across Sessions and Reboots
Given a device tagged as "Curb Captain Board" for Org A When the browser/app restarts or the OS reboots Then the station identity loads from secure local storage before the login screen renders And the tag is cryptographically bound to Org A; logging into Org B on the same device starts in restricted (untagged) mode And if local storage is cleared or the tag fails integrity verification (signature mismatch), the device enters restricted mode and logs an integrity error within 5 seconds
Admin-Verified Re-Tagging with Audit Trail
Given a tagged device When a user initiates re-tagging Then admin approval by an Org A manager with 2FA is required before proceeding And successful re-tagging requires a fresh valid provisioning link for the new station type and passing attestation And all attempts (success/failure) are logged with actor, from/to station, device fingerprint, and timestamp within 5 seconds And re-tagging is rate-limited to 3 attempts per device per 24 hours; excess attempts return HTTP 429
Lightweight Device Attestation and Fail-Safe Fallback
Given a provisioning or login event on a tagged device When minimum attestation checks fail (e.g., origin mismatch, unsupported secure storage, clock skew > 5 minutes) Then the device is treated as untagged and only restricted mode is available for that session And a security alert entry is recorded with reason and device fingerprint within 5 seconds And when attestation later passes on a subsequent attempt, the prior station context is restored without re-provisioning
Restricted Mode on Uncertain Context
Given an untagged device or a tagged device with ambiguous context (e.g., corrupted storage, geofence/station rule conflict) When a staffer reaches the login screen Then only the restricted role is available and all privileged endpoints return HTTP 403 And the UI displays a non-blocking banner prompting re-provisioning with a link to instructions And station-specific actions (bay assignment, expo bump/complete) are hidden or disabled
Station-Driven Login Experience and Least-Privilege Role Mapping
Given a correctly tagged device for a supported station type When a staffer with valid credentials logs in Then within 2 seconds the user is routed to the station’s default screen (Expo KDS for Expo Tablet; Curb Queue for Curb Captain; FOH Dashboard for Front-of-House) And only permissions for that station are granted; out-of-scope actions are hidden or disabled and server enforces with HTTP 403 on access attempts And an audit log records station type, role assigned, and route taken within 5 seconds
Role-Based UI Auto-Launch
"As a team member, I want to be taken straight to the screen for my role so that I can start working immediately."
Description

Automatically routes authenticated users to the correct UI module (e.g., Expo View, Curb Captain Board, Host Queue) and applies fine-grained permissions to show only allowed actions. Persists the route across refresh, supports hot-switch on role change, and gracefully handles unauthorized navigation attempts. Delivers zero-click onboarding at shift start and reduces time to task.

Acceptance Criteria
Auto-Launch to Correct Module at Login
Given a user authenticated successfully and scheduled as Expo on an Expo-tagged device, When the session is established, Then the app routes to /expo within 2 seconds without any user clicks and shows the Expo View as the primary screen. Given a user authenticated successfully and scheduled as Curb Captain on a Curb Board device, When the session is established, Then the app routes to /curb within 2 seconds without any user clicks and shows the Curb Captain Board as the primary screen. Given a user with multiple concurrent roles, When they log in from a device with a station tag, Then the route is chosen using station context over schedule priority and the decision is logged with station and role inputs. Given a user with no eligible role for the current location, When authentication succeeds, Then the app shows a No Access screen and does not route to any operational module.
Fine-Grained Permissions Enforcement
Given a user in the Expo role on /expo, When the UI renders, Then only Expo-permitted actions (e.g., Bump, Reprint, Mark Ready) are visible and enabled, and disallowed actions (e.g., Refunds, Settings) are not rendered. Given any attempt to invoke a forbidden API action by URL or UI, When the request is made, Then the server responds 403 and the UI displays a Not authorized message without state changes. Given a permissions model change, When the page is reloaded, Then the visible actions immediately reflect the updated permissions without stale controls. Given a blocked action, When it occurs, Then an audit event is recorded with userId, roleId, actionId, timestamp, and outcome=denied.
Route Persistence Across Refresh
Given a user currently on /curb with a valid session and role allowing /curb, When the browser refreshes, Then the app restores the route to /curb and rehydrates view state (selected bay filter) from session storage. Given a user refreshes while their role has changed to disallow the current route, When the app initializes, Then it redirects to the new allowed default route and shows a Role changed notification. Given a session expiration during refresh, When the user re-authenticates, Then the app routes to the last known route if still permitted; otherwise to the allowed default route.
Live Role Hot-Switch During Session
Given a user is active on /expo, When their effective role changes to Host via schedule update or station change, Then within 5 seconds the UI navigates to /host and displays a Role changed banner. Given a role hot-switch occurs while an action is in-flight, When the switch completes, Then committed actions are preserved, in-progress ephemeral UI state is cleared, and the user is shown the new module. Given a role hot-switch results in loss of access to the current module, When navigation happens, Then no 4xx error pages are shown and the user lands on the first allowed module for the new role.
Unauthorized Navigation and Deep Link Handling
Given a user with only Curb permissions opens a deep link to /settings, When the app loads, Then the user is redirected to their default allowed module and a Not authorized toast is shown within 1 second. Given a user navigates directly to a forbidden route via the address bar, When routing evaluates, Then the route guard blocks it, returns HTTP 403 for any API calls, and no restricted data is rendered. Given the user presses the browser Back button after a blocked navigation, When history is applied, Then the user remains on an allowed route (no navigation to the forbidden path).
Device/Station Context Resolution and Tie-Breakers
Given a user has overlapping Expo and Curb roles at the same time, When logging in on a device tagged Station=CURB, Then the selected module is /curb; when logging in on Station=EXPO, Then the selected module is /expo. Given station context is unavailable, When the user logs in, Then the selected module falls back to the scheduled primary role as defined by business rules and the fallback reason is logged. Given location rules restrict cross-site access, When a user logs into a different site than scheduled, Then the user is routed to a No Access or Location Select screen (per config) and no operational module is shown.
Performance and Observability of Auto-Launch
Given median restaurant Wi‑Fi and target hardware, When a user authenticates, Then time from auth success to first contentful paint of the target module is ≤ 2.0s at p95 and ≤ 1.0s at p50. Given the auto-launch decision service, When it evaluates schedule, station, and location inputs, Then decision latency is ≤ 200ms at p95 and emits a structured log with selectedRole, selectedModule, inputsHash, and latencyMs (no PII). Given feature flag autoLaunchEnabled=false, When users authenticate, Then the system lands on a neutral Home screen and requires manual module selection.
Location Rule Engine & Geofencing
"As a general manager, I want to set location-based rules that influence role assignment so that staff near the curbside area get curb-focused permissions automatically."
Description

Enables admins to define location-aware policies (Wi‑Fi SSID, IP/subnet, GPS/geofence) and time-of-day conditions that influence role selection when schedule or device context is ambiguous. Supports precedence, store-level overrides, and safe defaults. Prevents misassignment when devices move or stations are temporarily repurposed, increasing assignment accuracy.

Acceptance Criteria
SSID-Based Role Assignment on Store Wi‑Fi
Given a staffer logs in at Store A on a device connected to Wi‑Fi SSID "CurbPing-Expo" When schedule and device context do not uniquely determine a role Then the engine assigns role "Expo" within 2 seconds of login Given the same device connects to SSID "Guest" When the staffer logs in Then the SSID rule does not apply and no role is assigned based on SSID Then the assignment audit log records rule type "SSID", rule name, store ID, evaluated inputs, and selected role
IP/Subnet Rule Overrides Device Context
Given the device IP address is 10.10.20.34 within Store A subnet rule 10.10.20.0/24 mapping to role "Curb Captain" When device metadata suggests role "Expo" Then the engine assigns role "Curb Captain" per precedence and completes within 2 seconds Given a login from 10.10.30.12 that does not match any configured subnet When the staffer logs in Then no subnet rule is applied Then the audit log includes the precedence path showing subnet rule win over device context with rule IDs
GPS Geofence Triggers Curb Captain Role
Given a 60 m radius geofence around Store A curbside lot mapping to role "Curb Captain" When a staffer logs in from a device with GPS location within the geofence Then the engine assigns role "Curb Captain" within 2 seconds When the device reports a location outside the geofence by more than 10 m Then the geofence rule does not apply When the device denies location permission Then the engine proceeds without geofence input and evaluates remaining rules per precedence
Time‑of‑Day Condition Modifies Role Selection
Given a time window rule 16:00–21:00 local store time mapping to role "Curb Captain" When a staffer logs in at 16:00:30 Then the engine assigns role "Curb Captain" When a staffer logs in at 15:59:30 Then the time‑of‑day rule does not apply Then the engine evaluates time windows using the store’s configured timezone and logs the evaluated time and timezone
Precedence Resolution Across Conflicting Rules
Given matching Time‑of‑Day, SSID, and GPS rules and the configured precedence order is Store Override > Time‑of‑Day > Network (SSID/IP) > GPS > Device > Schedule > Default When a staffer logs in and all three match Then the engine assigns the role from the Time‑of‑Day rule and logs the precedence decision When two rules on the same precedence level match (e.g., SSID and IP) Then the engine selects the more specific match (exact SSID over subnet) and logs the tie‑break reason Then the audit output lists all evaluated rules, their outcomes, and the final selected role with timestamps
Store‑Level Override Applies Without Cross‑Store Impact
Given a Store B override mapping SSID "CurbPing-Expo" to role "Expo+" When a staffer logs in at Store B on that SSID Then the engine assigns role "Expo+" and records that a store‑level override was applied When a staffer logs in at Store A on the same SSID Then the Store B override does not apply and Store A’s local rules are used Then cross‑store logins reflect only the current store’s rule set and store ID in audit entries
Safe Default Role When No Rules Match or Device Moves
Given no schedule, device, SSID, IP/subnet, GPS, or time‑of‑day rules match When a staffer logs in Then the engine assigns the configured safe default role "Limited Access" and logs that no rules matched When the device location context changes mid‑shift causing prior location rule to no longer match Then the engine does not downgrade below the current role without explicit admin policy allowing downgrades and logs the attempted change Then the engine exposes a metric counting safe‑default assignments per store for monitoring
Manager Override & Audit Trail
"As a shift manager, I want to override a teammate’s auto-assigned role for a short period so that I can adapt to unexpected staffing changes."
Description

Allows authorized managers to temporarily override an auto-assigned role with a reason, scope, and expiration. Records the decision rationale, original inputs (schedule, station, location), and all permission changes in an immutable audit log. Notifies affected users and reverts automatically on expiry. Provides accountability while enabling rapid on-shift adjustments.

Acceptance Criteria
Manager Overrides Auto-Assigned Role During Rush
Given a staff member has been auto-assigned a role by Smart Role Match at location L and an authenticated manager M has override permission When M initiates an override for the staff member Then the system displays the current role and a list of allowable target roles based on least-privilege policy And M must enter a reason, select scope, and set an expiration time And upon confirmation, the staff member’s role changes to the selected role within 2 seconds And the staff member’s UI is redirected to the correct screen for the new role without requiring re-login And the original role and Smart Role Match inputs (schedule, station/device context, location rules) are captured for auditing
Override Input Validation and Constraints
Given M opens the override form for a user When M attempts to save without a reason Then Save is disabled and a message "Reason is required" is shown When M sets an expiration earlier than now or later than 8 hours from now Then the form shows a validation error and prevents save When M selects scope Then scope must be one of [User Session, Device, Location Shift] and only one can be selected And if scope = Device, the device must match the current device context or the override cannot be saved
Immutable Audit Log Entry for Override
Given an override is successfully applied Then an audit record is appended with fields: override_id, timestamp, manager_id, staff_id, location_id, device_id (if any), original_role, new_role, reason, scope, expiration, schedule_snapshot_id, station_context, location_rules_version, permission_diff And the record is write-once (no update/delete operations allowed via UI or API) And the record is tamper-evident via a hash of the record chained to the previous record And any cancel/revoke/expire event creates a separate audit record linked to override_id And an audit viewer can retrieve the record by override_id and see all related events
Notifications to Affected User and Managers
Given an override is applied or revoked or expired Then the affected staff member receives an in-app banner immediately and an SMS within 10 seconds if a verified phone exists And the initiating manager receives an in-app confirmation within 2 seconds And a notification entry is logged to the audit log with delivery status And if SMS delivery fails, the system retries up to 3 times over 5 minutes and records the final status
Automatic Reversion on Expiration
Given an active override with an expiration time T When current time >= T Then the system reverts the staff member to the original auto-assigned role within 30 seconds And the staff member UI updates to the correct screen without logout And an audit record of the reversion is appended And the staff member and initiating manager are notified of the reversion And if the staff member is offline, the reversion is applied at next login before any permissions are granted
Early Revoke and Conflict Handling
Given an active override exists for user U at location L When an authorized manager attempts to create a second overlapping override for U at L Then the system blocks the action and offers a "Replace existing override" option And when "Replace" is confirmed, the existing override is revoked and a new override is created atomically within 3 seconds When a manager with permission revokes an active override Then the user’s original role is restored within 2 seconds and an audit record is appended
Audit Log Access, Filters, and Export
Given a user with audit-view permission opens the audit log When they filter by staff_id, manager_id, location_id, date range, outcome (applied/revoked/expired), or role Then the filtered results return within 2 seconds for up to 10,000 records And export to CSV produces a file with the selected records and fields within 10 seconds And users without audit-view permission cannot access any audit data (403) And all audit log reads are themselves recorded with viewer_id, timestamp, and filter parameters
Fail-Safe Defaults & Offline Cache
"As an operator, I want sensible fallbacks when the internet or integrations are down so that staff can keep working without excessive access."
Description

Defines deterministic fallbacks when integrations or connectivity degrade: cache recent schedules, remember last-known good role within policy, or assign a safe default with limited permissions. Surfaces clear user messaging, triggers admin alerts, and captures telemetry for diagnosis. Ensures operations continue without over-permissioning during outages.

Acceptance Criteria
Offline Login With Cached Schedule
Given the device has no internet connectivity and a schedule cache exists updated within the last 24 hours When a staff member enters valid credentials at login Then the system assigns the last-known-good role that matches the cached shift and device context under least-privilege policy And restricts access strictly to the permissions of that role And displays an in-app banner within 1 second: Working offline using cached schedule from [timestamp] And records telemetry event role_assignment_fallback=cache including user_id, device_id, cache_age_minutes, location_id, timestamp And queues the telemetry to local storage for upload upon reconnection
Offline Login With No Valid Cache
Given the device has no internet connectivity and no schedule cache exists or the cache is older than 24 hours When a staff member enters valid credentials at login Then the system assigns the Safe Default role with least-privilege permissions and no access to admin or payout actions And directs the user to the Safe Default landing screen appropriate to the device profile within 2 seconds And shows an in-app alert: Limited access offline, no schedule found with a Retry action And records telemetry role_assignment_fallback=safe_default with reason=no_cache And triggers an admin alert via email/SMS within 60 seconds including location_id, device_id, and reason=no_cache
Schedule Service Degradation With Network Up
Given the device has internet connectivity but the schedule API fails with 5xx or times out after 3 seconds across up to 2 retries When a staff member logs in Then the system uses the last successful schedule response cached within 24 hours to assign role under least-privilege policy And shows a non-blocking toast: Using last known schedule service degraded And records telemetry role_assignment_fallback=stale_schedule including http_status, last_sync_time And sends a single consolidated admin alert per location per 15-minute window indicating schedule service degradation
Reconnect And Role Reconciliation
Given a user is logged in under a fallback role due to offline or degraded state And connectivity to required services is restored When the app successfully syncs and fetches current schedule and location rules Then the system recalculates the correct role within 2 seconds And if the recalculated role differs, transitions the UI to the correct home screen with no loss of locally stored in-progress actions And shows a banner: Permissions updated which can be dismissed and stays dismissed for this session And records telemetry role_reconciled with prior_role, new_role, reconciliation_latency_ms And removes any offline or degraded state banners
Device Context Safety Guard
Given the device is identified as an Expo tablet or a Curb Captain board via device profile When a fallback role must be assigned due to missing schedule or outage Then the assigned fallback role is the least-privilege variant permitted for that device profile (e.g., Expo-Restricted or Captain-Restricted) And the UI hides or disables all actions not permitted for that role And attempting a disallowed action results in a permission error within 100 ms and is logged And telemetry includes device_profile and enforced_capability_deltas
Local Cache Management And TTL
Given the app stores schedule and role-mapping data locally When a new schedule sync succeeds Then the cache is updated atomically and versioned with timestamp and hash And cache entries older than 24 hours are purged automatically And total cache size is limited to 1 MB per location; exceeding this triggers LRU eviction of oldest entries And offline cache reads return within 100 ms on baseline hardware And cache corruption detected by checksum mismatch forces Safe Default fallback and records telemetry cache_corrupt
Security Over-Permissioning Guardrails
Given any fallback path is taken (cached, stale, or safe default) When role assignment is executed Then no permission exceeding the policy-defined minimum for the device context and user group is granted And admin settings, user management, payouts/exports, and configuration screens are always blocked And automated tests include negative cases proving privileged actions are denied under fallback And a weekly report aggregates attempted privileged actions during fallback by location and user

GeoShift Lock

Time‑boxed magic links only activate within the restaurant’s geofence and on approved devices or stations. Blocks forwarded links and remote logins, auto‑invalidates if the tablet leaves the lot, and gives multi‑unit ops tighter control without extra steps for staff.

Requirements

Geofenced Link Activation
"As a curbside customer, I want my “I’m here” link to work only when I arrive at the restaurant so that arrivals are accurate and staff aren’t pinged when I’m not on site."
Description

Enforce activation of customer “I’m here” magic links only when the device is physically within a configurable geofence for the restaurant location. Uses browser geolocation with accuracy thresholds, network/IP heuristics as a secondary signal, and tamper‑resistant, single‑use tokens bound to order and phone number. Supports polygon or radius geofences per store, with configurable accuracy tolerance and a short grace window for edge cases. Provides clear customer messaging if location permissions are denied, plus a fallback flow to call the store when geolocation cannot be verified. Emits events for allowed/blocked activations to support analytics and ops monitoring. Integrates with existing CurbPing order state machine to only transition to “Arrived” on successful geofence validation.

Acceptance Criteria
Successful activation within geofence via browser geolocation
Given a Ready-for-Pickup order for store S with geofence G (polygon) and accuracy_threshold=50m and link_ttl=10m And an unconsumed magic link token T bound to {order_id, phone_hash} And the device shares browser geolocation with accuracy <= 50m and point-in-polygon(G)=true When the customer taps the “I’m here” link within link_ttl Then activation is allowed and order state transitions to Arrived within 1s And token T is marked consumed immediately after success And an activation_allowed event is emitted with outcome=allowed, method=geolocation, reason=inside_geofence and payload {order_id, store_id, geofence_id, token_id, phone_hash_prefix, lat, lon, accuracy_m, timestamp_iso} And the UI displays a visible confirmation state indicating arrival recorded
Block activation outside geofence with customer messaging and call fallback
Given a Ready-for-Pickup order for store S with geofence G and edge_grace_meters=30m And an unconsumed magic link token T And the device geolocation accuracy <= 50m with distance from geofence boundary > 30m outside When the customer taps the “I’m here” link Then activation is blocked and the order state remains unchanged And an activation_blocked event is emitted with outcome=blocked, method=geolocation, reason=outside_geofence and payload {order_id, store_id, geofence_id, token_id, phone_hash_prefix, lat, lon, accuracy_m, distance_to_boundary_m, timestamp_iso} And the UI shows a clear message that location cannot be verified at this store and provides a tel: link to call the store And the link remains valid until TTL expiry, allowing retry upon location change
Heuristic fallback allows activation without GPS when network/IP indicates in‑geofence
Given store S is configured heuristic_fallback=true with confidence_threshold=0.8 And browser geolocation is unavailable or times out after 10s And network/IP (and optional Wi‑Fi SSID) heuristics resolve to within geofence G with confidence >= 0.8 And token T is valid and unconsumed When the customer taps the “I’m here” link Then activation is allowed and the order transitions to Arrived And token T is consumed And an activation_allowed event is emitted with outcome=allowed, method=heuristic, reason=ip_wifi_in_geofence and payload {order_id, store_id, geofence_id, token_id, phone_hash_prefix, confidence, timestamp_iso}
Permissions denied or insufficient signals trigger guided fallback and block
Given geolocation permission is denied or geolocation accuracy > 50m after retrying for 10s And heuristic_fallback is disabled or confidence < 0.8 And token T is valid and unconsumed When the customer taps the “I’m here” link Then activation is blocked and the order state remains unchanged And the UI presents instructions to enable location plus a tel: link to call the store And an activation_blocked event is emitted with outcome=blocked, method=unknown, reason=location_unverified and payload {order_id, store_id, geofence_id, token_id, phone_hash_prefix, timestamp_iso}
Single‑use token and phone binding prevent forwarding and reuse
Given a magic link token T bound to order_id O and phone_hash P and expires_at within 10m of issuance When T is presented for activation Then the backend verifies T is unexpired, unconsumed, and bound to {O, P} And upon a successful activation T is consumed and cannot be reused And any subsequent request with T receives HTTP 410 Gone and emits activation_blocked with reason=token_reuse And any request presenting T with a different bound phone_hash P2 != P is blocked and emits activation_blocked with reason=token_binding_mismatch And no blocked attempt alters the order state
Edge handling: accuracy radius overlap and grace distance near boundary
Given accuracy_threshold=50m, edge_grace_meters=30m, edge_grace_seconds=120s And the first geolocation fix has accuracy=70m and center 10m outside geofence G such that the accuracy circle intersects G When the customer taps the “I’m here” link within edge_grace_seconds of that fix Then activation is allowed and the order transitions to Arrived And an activation_allowed event is emitted with method=geolocation, reason=edge_grace_overlap and payload {order_id, store_id, geofence_id, token_id, phone_hash_prefix, lat, lon, accuracy_m, timestamp_iso} And if subsequent fixes within 10s still do not intersect G and center distance > edge_grace_meters, the activation is blocked with reason=outside_geofence
Idempotency and state‑machine integration
Given an order O transitions due to a valid geofenced activation When duplicate activations for O occur within 5 minutes (same or different device) Then no additional state transitions are recorded and the API returns 200 OK with idempotent=true And only orders in {Ready for Pickup, Out for Pickup} may transition to Arrived; other states return 409 Conflict with no change And exactly one event is emitted per successful activation and no duplicate events are produced And both allowed and blocked events include correlation_id to support analytics linking
Time-Boxed Magic Link TTL
"As a customer, I want my arrival link to be valid only around my pickup time so that I don’t accidentally trigger an arrival too early or after my order is closed."
Description

Issue short‑lived, single‑use magic links with configurable time‑to‑live (TTL) tied to the pickup window. Automatically extend validity within a defined pre‑arrival window (e.g., 15 minutes before scheduled pickup) and expire upon successful activation or after the pickup window closes. Handles clock skew via server timestamps and includes replay protection. Displays friendly expiration messages with a one‑tap request‑new‑link action via SMS if needed. Integrates with notification service and order scheduler to align TTL with prep/hold times, keeping food hot and reducing premature pings.

Acceptance Criteria
Issue Single‑Use Magic Link With Configurable TTL
Given an order with a scheduled pickup window [start, close] and a configured magic_link_ttl_minutes When a magic link is sent for the order Then the system creates a unique, single‑use token tied to the order and customer And sets expiration_at = min(server_now + configured_ttl, pickup_window_close) And persists token metadata (order_id, customer_id, expiration_at, status=pending, created_at) server‑side And delivers a URL containing the token via the notification service And any request using the URL after expiration_at is rejected with reason=expired
Auto‑Extend Validity Within Pre‑Arrival Window
Given a magic link with expiration_at and a configured pre_arrival_window_minutes And server_now is within [pickup_window_start - pre_arrival_window, pickup_window_close] When the customer opens the link and token status is pending Then the system extends expiration_at to pickup_window_close if expiration_at < pickup_window_close And records an extension event with old and new expiration_at And the token remains single‑use and activates normally within the new validity And extensions never exceed pickup_window_close
Immediate Expire On First Successful Activation
Given a pending magic link that has not expired by server_time When the customer successfully activates the link Then the system marks the token status=consumed with consumed_at=server_time And immediately invalidates the token for any subsequent requests And emits an activation event to staff with order context And if server_time >= pickup_window_close before activation, the token is expired and returns 410 with reason=window_closed
Server‑Time Authority and Clock Skew Handling
Given client clocks may be skewed by up to skew_tolerance_minutes When evaluating token validity and rendering countdowns Then all validity decisions use server_time only And the client UI fetches server_time and expiration_at to calculate countdown And an activation attempt within skew_tolerance after client‑perceived expiry is accepted if server_time <= expiration_at And any attempt where server_time > expiration_at is rejected with reason=expired
Replay Protection and Race‑Safe Single Use
Given multiple opens or clicks of the same magic link can occur concurrently When two or more activation requests for the same token arrive nearly simultaneously Then exactly one request succeeds (first‑commit wins) and all others are rejected with reason=replayed And subsequent requests for a consumed token return 409/410 with a friendly message And tokens are signed and tamper‑evident; altered or unsigned tokens are rejected with reason=invalid_token
Friendly Expiry Message and One‑Tap Request‑New‑Link via SMS
Given a user opens a magic link that is expired, consumed, or window_closed When the server determines the token is not usable Then the client shows a friendly message including reason and relative time (e.g., "expired 3m ago") And displays a "Text me a new link" action if the pickup window is still open And on tap, a fresh magic link is sent via SMS within 5 seconds and the event is logged And if the pickup window is closed, the action is hidden and guidance to contact the store is shown
TTL Alignment With Scheduler and Notifications
Given the order scheduler provides prep_time, ready_time, hold_until, and pickup_window [start, close] When the scheduler updates any of these times (e.g., delay or early ready) Then expiration_at updates to reflect the new pickup_window_close (shorten or extend within pre_arrival rules) And previously issued links honor the new expiration without requiring a new URL And notification payloads include expiration_at and pickup_window_close for observability And a staff notification is emitted if expiration shortens to avoid premature pings
Device Whitelist & Station Binding
"As an operator, I want to restrict which tablets can view and act on arrivals so that only approved stations handle orders and bay assignments remain consistent."
Description

Allow operators to approve specific staff devices or browser stations per location and bind staff sessions to those devices for arrival dashboards. Enforce sign‑in only from whitelisted device fingerprints and known IP ranges (optional), with easy QR-based device enrollment and revocation in the manager console. Associate each device with a numbered bay or station to streamline assignment. Tokens are short‑lived, rotate automatically, and are invalidated on device removal. Requires no extra taps during normal ops; once enrolled, staff open the dashboard and it just works.

Acceptance Criteria
QR Enrollment and Station Binding
Given I am a manager in the console for Location A and generate an enrollment QR for Station 3 And a new, unenrolled device scans the QR in a supported browser When the device opens the magic enrollment link within the geofence Then the system captures the device fingerprint and whitelists it for Location A And binds the device to Station 3 And issues a short-lived session token and refresh token tied to the device fingerprint And the device appears in the console Devices list as Active with Station 3 And the device sees the Arrival Dashboard without additional prompts
Sign-in Enforcement: Whitelist and Optional IP Range
Given a non-whitelisted device attempts to access the Arrival Dashboard link When it requests sign-in within the geofence Then access is denied with an error stating the device is not approved And no session token is issued And an audit event logs the attempt with device fingerprint and IP address Given a whitelisted device attempts access from an IP outside the configured ranges while IP enforcement is enabled When it requests sign-in Then access is denied with an error stating IP not allowed And an audit event records policy block due to IP range Given a whitelisted device within the allowed IP range and geofence When it requests sign-in Then the session is established successfully
Zero‑Tap Daily Start and Station Presence
Given an enrolled, whitelisted device is within the location geofence When staff opens the dashboard URL Then the session resumes or silently signs in using the rotated token without extra taps And the dashboard header displays the device’s assigned Station number And new arrivals default to that Station in assignment workflows And staff can override assignment per order without changing the device binding
Token Lifecycle, Rotation, and Binding
Given a whitelisted device is signed in When the access token lifetime elapses Then the client refresh flow rotates tokens without user interaction And expired tokens are rejected by the API And previously rotated tokens cannot be reused (replay is blocked) And tokens are cryptographically bound to the device fingerprint and location Given the device is removed from the whitelist in the console When the removal is saved Then all active tokens for that device immediately fail authorization And the device is required to re-enroll to regain access
Device Revocation and Immediate Cutoff
Given a manager revokes or deletes a device in the console When the change is saved Then the device status updates to Revoked in the console list And any active dashboard session on that device is forced to sign out on next request And subsequent API calls from that device return 401 Unauthorized And the device no longer appears in the Active Stations roster And a revocation audit log entry is recorded
Geofence Enforcement on Staff Sessions
Given a whitelisted device with an active session exits the defined geofence When the device attempts to view arrivals or take actions Then the UI displays a Geofence required message and hides arrival details and customer PII And API requests are rejected due to geofence policy Given the device re-enters the geofence When the dashboard is brought to foreground or refreshed Then the session regains access without re-enrollment or extra taps
Link Forwarding and Remote Login Blocking
Given an enrollment or session link is forwarded to a different device When the recipient device opens the link Then access is denied due to fingerprint mismatch and no session is created And an audit event records a blocked forwarded-link attempt Given the original, whitelisted device opens the same link within the geofence When it requests access Then the link activates as expected and the session is granted
Forwarded Link & Remote Login Block
"As an operator, I want forwarded or spoofed arrival links to be blocked so that only the actual customer on site can trigger an arrival."
Description

Bind customer magic links to the original SMS recipient and device context to prevent forwarding or remote activation. Validate via one‑time tokens tied to phone number, user agent and OS signals, and geofence match. Rate‑limit attempts, detect anomalous patterns (e.g., different city/IP), and present a reverify flow that resends a fresh link to the original number when misuse is detected. For staff dashboards, block remote logins from non‑whitelisted devices and unknown networks. Log all blocked events for audit and support.

Acceptance Criteria
Customer magic link blocked when forwarded to a different device
Given a magic link token T was sent via SMS to phone number P And T is bound to P’s context (token ID, intended phone hash, initial device fingerprint, and OS family) When T is opened on device D2 whose fingerprint differs from the initial context beyond 30% similarity Then activation is blocked and the arrival flow is not shown And the user sees an error message “This link can only be used by the original recipient” And token T is marked as suspected-forward and cannot be activated on D2 And a reverify option is presented
Customer magic link activation restricted to restaurant geofence
Given a restaurant geofence radius of 150 meters around location R And a valid magic link token T for order O When the link is opened and the device location is >150 meters from R (based on GPS with accuracy ≤100m, else IP geolocation) Then activation is blocked and the UI shows “You’re not at the pickup location yet” And when the device reports a location within ≤150 meters of R Then activation succeeds within 2 seconds and the arrival is published to staff And all geofence decisions are recorded with location source and accuracy
Attempt rate limiting on magic link activation
Given a rate limit of 5 blocked or failed activation attempts per 10 minutes per token and per IP When attempts exceed the limit for token T or IP X Then further activation attempts are blocked for 15 minutes with status 429 and a user-friendly message And the UI does not reveal which signal failed (device, geofence, or IP) And the lockout window resets after 15 minutes or on successful reverify, whichever occurs first
Anomalous location/IP triggers reverify and blocks activation
Given a valid token T associated with restaurant R When T is opened from an IP geolocated >50 km from R or from an ASN flagged as proxy/VPN/hosting Or when device fingerprint deviates from the initial context beyond 30% similarity and city mismatch is detected Then activation is blocked and a reverify prompt is shown And the system sends a reverify SMS to the original phone number P only And an audit event with reason REMOTE_IP_MISMATCH or DEVICE_CONTEXT_MISMATCH is recorded
Reverify flow sends fresh link to original number and resolves false positives
Given a misuse flag on token T for phone number P When the user selects Reverify Then a new token T2 is generated and SMSed to P within 5 seconds And T is immediately invalidated and cannot be used And when T2 is opened on a device within the geofence Then activation succeeds and the arrival is published And all events (reverify requested, T invalidated, T2 delivered, activation outcome) are logged with timestamps
Staff dashboard remote login blocked on non-whitelisted devices and networks
Given a staff dashboard that enforces device allowlist and network allowlist for restaurant R When a login attempt occurs from a device not on the allowlist or from an IP not on the network allowlist Then login is blocked with an inline error “This device or network is not approved” And an audit event with reason STAFF_DEVICE_NOT_ALLOWED or STAFF_NETWORK_NOT_ALLOWED is recorded And if a previously logged-in station tablet moves >200 meters outside the geofence Then it is auto-signed out within 30 seconds and further access is blocked until back within geofence or on an approved network
Audit logging for blocked and reverify events
Given the system must log all blocked activations and staff login denials When a block, rate-limit, or reverify event occurs Then an audit record is created within 5 seconds containing: timestamp (UTC), restaurant ID, event type, reason code, token ID, masked phone (last 4), device fingerprint hash, IP, geo city/region, and action taken And audit records are queryable in the admin console within 60 seconds and exportable as CSV And a correlation ID is present to link customer- and staff-side events for the same order
Perimeter Breach Auto-Invalidate
"As an operations manager, I want staff sessions to auto‑disable when a device leaves the lot so that our order data and controls aren’t exposed off‑premises."
Description

Continuously verify that active staff dashboard sessions remain within the store’s geofence. If a whitelisted device leaves the perimeter (e.g., tablet taken offsite), immediately pause live arrivals view, revoke tokens, and require revalidation on return. Provide a brief offline/grace period for jittery GPS, with multi‑signal checks (location plus network changes) to reduce false positives. Notify managers of repeated breaches. Ensure customer flows are unaffected; only staff session capabilities are limited until the device is back on site.

Acceptance Criteria
Offsite Exit Triggers Immediate Session Suspension
Given an active staff dashboard session on a whitelisted device within the store geofence And the grace period is configured to 30 seconds When the device location is outside the geofence continuously for longer than the grace period And at least one secondary signal corroborates the exit (e.g., Wi‑Fi SSID change, loss of store Wi‑Fi, WAN IP change) Then pause the live arrivals view within 2 seconds And revoke access and refresh tokens for the session immediately And disable staff-only actions with an “Offsite — session paused” banner And write an audit log entry capturing timestamp, device ID, geofence ID, and signals used
GPS Jitter Does Not Cause False Positives
Given the device remains physically on-site connected to a whitelisted store network When the GPS reports intermittent coordinates outside the geofence totaling less than the configured grace period within any 60-second window Then do not pause the session or revoke tokens And surface no offsite banner or blocking modal And record a non-actioned “signal drift” metric only (no breach event)
Multi‑Signal Exit Determination and Degraded Mode
Given multi-signal monitoring is enabled (GPS + Wi‑Fi SSID/BSSID + WAN IP) When GPS indicates exit but Wi‑Fi SSID/BSSID is whitelisted and WAN IP matches store range Then classify as inconclusive and do not invalidate When GPS indicates exit and either Wi‑Fi is disconnected or SSID/BSSID is not whitelisted or WAN IP changes to non-store range Then invalidate per suspension flow When all signals are unavailable for longer than the grace period Then invalidate and mark reason as “signals lost > grace”
On‑Site Return and Revalidation Restores Access
Given a session was suspended due to offsite exit When the device re-enters the geofence and at least one secondary signal matches store (Wi‑Fi SSID/BSSID or WAN IP) Then prompt the user for one-tap revalidation on the same device And upon success, rotate tokens and restore the live arrivals view within 2 seconds And preserve unsent local updates and reconcile without duplication And append an audit log “revalidated” event with duration offsite
Manager Notified on Repeated Breaches
Given breach events are logged per device and store When a device incurs 3 or more breach events within 24 hours Then send a manager alert via configured channels (SMS/email/in-app) within 1 minute And include device label, store, timestamps, and last known location And suppress duplicates to at most 1 alert per device per hour And record notification delivery status
Customer Flows Unaffected During Staff Suspension
Given a staff session is suspended for offsite exit When customers trigger “I’m here” arrivals or receive SMS updates Then customer messaging, arrival intake, and downstream webhooks continue without interruption And arrivals are queued and become visible to staff immediately upon revalidation And no customer-facing UI shows errors related to staff suspension And end-to-end arrival intake success rate remains ≥ 99.9% during suspension window
Multi-Unit Policy Controls & Audit Trail
"As a multi‑unit operator, I want to centrally set and audit geofence and link policies across all stores so that security is consistent without adding steps for staff."
Description

Provide a central console for multi‑unit operators to define and deploy GeoShift Lock policies at org, region, or store level: geofence size/shape, link TTL and grace windows, device enrollment rules, and network/IP allowlists. Support per‑location overrides with inheritance, bulk actions, and versioned policy changes. Expose an audit trail of key security events (blocked activations, device enrollments, perimeter breaches) with export and webhook capabilities for SIEM. No additional steps required for frontline staff; policies apply transparently at runtime.

Acceptance Criteria
Org/Region/Store Policy Hierarchy & Inheritance
Given an org with 2 regions and 5 stores, When an admin publishes a GeoShift Lock policy at org level, Then all child regions/stores show the policy as Effective with source=Org within 60 seconds. Given a region override is published, When viewing a store in that region, Then Effective value reflects region override with source=Region and org values remain unchanged. Given a store override is removed, When policy is republished, Then Effective values revert to parent within 60 seconds and an "override_removed" audit event is recorded. Rule: Conflicts resolve by nearest ancestor (Store overrides Region, Region overrides Org). Rule: Effective policy view displays each setting (geofence, TTL, grace, device rules, allowlists) with its source and version.
Bulk Actions Deployment
Given an admin selects 50 stores across 3 regions, When applying a policy change via Bulk Apply, Then success rate is ≥ 99% with per-store status and failures retriable individually. Given mixed connectivity, When bulk publish completes, Then stores unreachable are marked "Pending" with auto-retry every 5 minutes up to 12 hours. Given a dry-run is requested, When previewing, Then the system lists impacted stores, overridden fields, and any policy conflicts without persisting changes. Rule: Bulk operations are idempotent via client-supplied operation_id; replays do not create duplicate versions.
Geofence and Link TTL/Grace Windows Controls
Given a polygon geofence and link TTL=15 minutes with grace=5 minutes are set, When a customer activates a magic link outside the polygon, Then activation is blocked with reason=outside_geofence and an audit event is recorded. Given the link is used within TTL+grace inside the polygon, When activation occurs on an approved device, Then activation succeeds and staff workflow remains unchanged (no additional prompts). Rule: p95 arrival processing latency overhead from policies ≤ 100 ms versus baseline. Given a registered station tablet exits the geofence, When detection occurs, Then all active sessions on that device auto-invalidate within 30 seconds and a "perimeter_breach" audit event is emitted.
Device Enrollment Rules & Enforcement
Given device enrollment is restricted to approved device IDs and OS versions, When an unapproved device attempts to activate a link, Then activation is blocked with reason=unapproved_device and logged. Given a device is unenrolled by an admin, When unenrollment is published, Then any active sessions on that device are revoked within 30 seconds and subsequent activations are blocked. Rule: Enrollment and unenrollment actions require MFA and are recorded with actor, timestamp, device fingerprint, and location.
Network/IP Allowlists Enforcement
Given a store has an IP allowlist [CIDR1, CIDR2], When an activation originates from an IP outside the allowlist, Then it is blocked with reason=ip_not_allowed and an audit event is captured. Given dual-stack networks, When activation occurs from IPv6 in an allowed CIDR, Then it succeeds equivalently to IPv4. Rule: IP changes during an active session trigger re-evaluation; if now disallowed, session terminates within 30 seconds.
Audit Trail Events, Filtering, and Export
Rule: The system records events for blocked activations, device enrollments/unenrollments, policy publish/rollback, and perimeter breaches with fields: event_type, org_id, location_id, actor_id (if any), device_id, ip, geo_coordinates (if available), reason_code, policy_version, and timestamp (ISO 8601 UTC). Given an auditor filters by date range, event_type, and location, When results are exported, Then a CSV and JSON export are available with column headers, pagination support, and row counts matching the on-screen total. Rule: PII fields are redacted or tokenized per org privacy settings in both UI and exports.
Webhook Delivery to SIEM
Given a webhook is configured with URL and secret, When eligible events occur, Then the system delivers JSON payloads signed with HMAC-SHA256 including an idempotency key and policy_version. Rule: Delivery is at-least-once with ordered delivery per location; retries use exponential backoff up to 24 hours with DLQ for manual replay. Given the SIEM returns 2xx, When delivery completes, Then the event is marked delivered; for 4xx/5xx, Then retries follow policy and are visible in delivery logs with reason codes.

Roster Sync

Pulls schedules from tools like 7shifts/When I Work/Toast and pre‑sends magic links before each shift. Handles swaps and call‑ins automatically, starts sessions at clock‑in, and auto‑logs out at shift end—reducing admin pings while giving owners clean attendance and usage visibility.

Requirements

Schedule Provider Connectors
"As an owner-operator, I want CurbPing to connect to my scheduling tool so that shifts and staff automatically sync without manual entry."
Description

Build authenticated integrations with 7shifts, When I Work, and Toast to ingest schedules, employee profiles, locations, roles, and timezones into CurbPing’s canonical model. Support OAuth/API-key auth, least-privilege scopes, webhook subscription for schedule and clock events, and a polling fallback with rate-limit handling. Map provider entities to CurbPing objects, normalize timezones, and de-duplicate shifts across multiple locations. Provide multi-location linking, sandbox/test connections, and clear error surfaces for failed syncs.

Acceptance Criteria
Provider Authentication and Connection Setup
Given a restaurant admin selects 7shifts or When I Work, When they complete OAuth, Then the connection is marked Connected and access/refresh tokens are stored encrypted at rest and retrievable for API calls. Given a restaurant admin selects Toast, When they enter a valid API key or complete OAuth (if supported), Then the credential is validated via a test API call and stored encrypted with secrets masked in the UI. Given access tokens expire, When a background refresh is needed, Then tokens refresh automatically and failures surface as Re-authentication required with provider-specific error codes. Given multiple provider accounts, When the admin connects a second account, Then CurbPing preserves both connections without overwriting credentials and labels each by provider account and location. Given an admin disconnects a provider, When confirmed, Then tokens/keys are deleted within 60 seconds and any provider webhooks are removed.
Least-Privilege Scope Enforcement
Given OAuth for 7shifts/When I Work, When the authorization URL is generated, Then only the minimum scopes required for employees, locations, schedules, and webhooks are requested (no extra scopes). Given the provider returns fewer scopes than required, When connection completes, Then CurbPing flags Insufficient scopes and blocks sync until scopes are corrected. Given the provider grants more scopes than the approved list, When connection completes, Then CurbPing ignores the extras and records an audit entry listing granted scopes. Given a call would require an ungranted scope, When attempted, Then it is blocked client-side with a clear error identifying the missing scope before any network request is made. Given scope definitions change, When CI runs, Then automated checks fail if new scopes are added beyond the approved baseline.
Webhook Subscription and Signature Verification
Given a successful connection to a provider that supports webhooks, When connecting, Then CurbPing registers subscriptions for schedule and clock/shift events and persists subscription IDs. Given a webhook delivery, When received, Then the request signature is validated per provider spec and invalid signatures are rejected with 401 and not processed. Given duplicate deliveries or out-of-order events, When processed, Then handling is idempotent and results in a single, correct update to the canonical record. Given a valid event, When processed, Then CurbPing responds 2xx within 2 seconds and applies the change within 5 seconds on average. Given a subscription is removed or fails handshake, When detected, Then an alert is raised and polling fallback is enabled for the affected location within 2 minutes.
Polling Fallback with Rate-Limit Handling
Given webhooks are unavailable or failing for a provider/location, When fallback is active, Then polling cadence respects provider rate-limit headers and results in zero 429 responses during a 30-minute test window. Given repeated 5xx or network errors, When polling, Then exponential backoff is applied (minimum 15s, maximum 10m) and polling resumes normal cadence after a successful request. Given endpoints support ETag/If-Modified-Since, When polling, Then conditional requests are used to avoid re-fetching unchanged resources. Given overlapping pages or successive runs, When the same record is encountered twice, Then deduplication prevents duplicate creation or updates. Given a valid webhook is received for the location, When verified, Then polling is disabled for that location within 2 minutes.
Canonical Mapping and Timezone Normalization
Given provider employees, locations, roles, schedules, and timezones, When ingested, Then each maps to a CurbPing canonical object with required fields populated and foreign keys resolved. Given shift start/end times in provider local time, When stored, Then times are normalized to UTC using the location timezone offset and DST rules. Given cross-midnight or overnight shifts, When normalized, Then end times remain after start times in UTC and render on the correct local calendar dates. Given missing or ambiguous timezone data, When encountered, Then the location’s configured timezone is used and the record is flagged for review. Given 100 sample records per provider, When validation runs, Then mapping error rate is 0 and timezone normalization is accurate to the minute.
Multi-Location Linking and Shift De-duplication
Given an employee exists in multiple provider locations, When imported, Then CurbPing maintains a single employee identity linked to all locations and roles. Given the same shift is received from multiple sources (API, webhook, or duplicate accounts), When ingested, Then duplicates are identified via {provider, provider_account, provider_employee_id, location_id, start_at, end_at} and only one canonical shift is stored. Given overlapping shifts for the same employee and time window, When detected, Then the conflict is flagged and not auto-merged. Given shifts are viewed by location, When queried, Then only shifts linked to that location are returned and used for attendance/session triggers. Given deduplication rules are updated, When a reindex job runs, Then prior duplicates are reconciled without data loss.
Sandbox/Test Connection and Clear Error Surfacing
Given an admin clicks Test connection, When executed, Then a non-destructive validation (auth check, minimal data fetch, webhook handshake) returns Pass/Fail with specific reasons. Given a sync failure (auth, scope, webhook create, rate limit, mapping), When it occurs, Then the UI displays a human-readable message, provider error code, timestamp, and next steps; logs include a correlation ID. Given any record-level failure, When retries are applicable, Then the system retries with capped attempts and exposes a downloadable CSV of failures without PII. Given a successful sync, When complete, Then the UI shows last sync time, counts of employees/locations/shifts updated, and active mode (webhook or polling) per location. Given an error is corrected, When Re-test is initiated, Then the connection status updates to Healthy within 60 seconds if validations pass.
Employee & Shift Mapping Rules
"As a shift manager, I want staff and shifts to map correctly in CurbPing so that only the right people get curbside responsibilities and links."
Description

Implement a matching engine that reliably links provider employees to CurbPing users using phone, email, and external IDs, with conflict resolution and merge flows. Normalize shift data (start/end, breaks, roles) and enforce location/role filters to include only relevant curbside staff. Handle daylight savings/timezone edge cases, overlapping shifts, and duplicate assignments. Provide an admin UI to review unmatched records, approve merges, and set default bay/role behaviors.

Acceptance Criteria
Deterministic Employee Matching by Identifiers
Rule: Normalize identifiers before matching (phone -> E.164; email -> lowercase/trim; externalId -> trim). Rule: Match priority = externalId > phone > email. Given a provider employee has at least one identifier, When a sync runs, Then if exactly one existing user matches on the highest-priority identifier, the employee is linked to that user. Then if no existing user matches any identifier, a new user is created and linked. Then if multiple users match on the same highest-priority identifier, the record is flagged for review (no auto-link).
Conflict Resolution and Merge Flow for Duplicate Users
Given a provider employee matches more than one CurbPing user by identifiers, When the sync completes, Then a Merge Review item is created containing all candidate users and the conflicting identifiers. Given an admin selects users to merge and confirms, When the merge executes, Then identifiers are unioned (deduped), externalId mappings are preserved by provider namespace, and historical shifts/sessions are reassigned to the surviving user. Then duplicate open sessions are closed, and only one active session remains. Then the action is audit logged with actor, timestamp, inputs, and outcome. Given an admin rejects merge and selects a single user to link, When saved, Then the employee is linked only to the selected user and the review item is resolved.
Shift Normalization: Start/End, Breaks, Roles
Rule: Convert all shift times to ISO 8601 with IANA timezone for the location; store UTC equivalents. Given a shift with start, end, break segments, and provider role code, When imported, Then duration = (end − start) − sum(breaks) and duration >= 0. Then break segments must not overlap; overlapping breaks cause the shift to be rejected with an error message. Then provider role code is mapped to a CurbPing role via the role map; unmapped role results in the shift marked Ineligible with reason = Unmapped Role. Then shifts missing required fields (start or end) are rejected with a descriptive error.
Location and Role Filtering for Curbside Eligibility
Rule: Only shifts whose provider location is mapped to the current CurbPing location are eligible. Rule: Only roles flagged curbside-eligible in the role map are eligible. Given a normalized shift, When eligibility is evaluated, Then shifts with unmapped locations are excluded and surfaced in Admin > Unmatched with reason = Unmapped Location. Then shifts with non-eligible roles are excluded with reason = Role Not Curbside. Then eligible shifts are marked Eligible and proceed to session automation.
Timezone and Daylight Savings Edge Cases
Given a location observing DST, When a shift spans the spring-forward transition (missing hour), Then timestamps are stored with correct offsets and duration reflects actual elapsed time (no negative or zero anomalies). Given a shift spans the fall-back transition (repeated hour), When normalized, Then the earlier and later 1:00 AM are disambiguated by offset, and duration equals actual elapsed time (no double-counting). Rule: All stored timestamps include timezone offset and UTC; computations use timezone-aware arithmetic.
Overlapping Shifts and Duplicate Assignments Handling
Given a user has overlapping shifts for the same location window from any provider source, When normalization completes, Then the primary shift is selected by latest provider updatedAt; others are marked Duplicate Overlap and excluded from automation. Given overlapping shifts across different locations for the same user and time window, When detected, Then the records are flagged for Admin Review: Overlap Across Locations (no auto-start). Given duplicate assignments (same user, role, time), When sessions are created at clock-in, Then only one active session is created; additional duplicates are ignored and logged.
Admin UI for Unmatched Records and Default Behaviors
Given unmatched employees or shifts exist, When an admin opens Admin > Roster Sync > Reviews, Then a paginated, filterable list shows items with columns: Type, Name, Source, Identifier(s), Reason, Created At, Actions. Given an unmatched employee due to ambiguous match, When the admin chooses Merge or Link and confirms, Then the system performs the action, updates mappings, and removes the item. Given roles require defaults, When the admin sets default bay per role and per location and saves, Then new eligible shifts inherit the correct default bay; active sessions for that role update within 30 seconds. Rule: All admin actions are audit logged (actor, timestamp, before/after).
Pre-Shift Magic Link Dispatch
"As a crew member, I want to receive my ‘I’m here’ magic link before my shift so that I can start helping customers without extra steps or apps."
Description

Automatically send personalized, single-use magic links to scheduled staff via SMS (primary) or email a configurable number of minutes before shift start. Support message templates, localization, quiet hours, opt-out compliance, and deduplication if a shift is edited. Include secure deep links with time-bound tokens, device-agnostic landing, and logging for delivery, open, and click events.

Acceptance Criteria
Pre-Shift SMS Dispatch Timing
Given a staff member has a scheduled shift with start time T and a send offset of O minutes When the current time reaches T minus O minutes in the staff member’s local timezone Then the system queues and sends a personalized SMS containing a unique magic link to the staff member’s phone number And the SMS content is rendered from the active template with populated placeholders And only one dispatch is sent per shift occurrence And if SMS is undeliverable within 60 seconds, the system attempts email fallback to the staff member’s email if available
Template Rendering and Localization
Given a staff member has a language preference L (or defaults to the location’s Ld) When generating the pre-shift message Then the system selects the localized template for L (or Ld if L is missing) And replaces placeholders {first_name}, {role}, {shift_start_time_local}, and {location_name} with correctly formatted locale-specific values And the rendered preview endpoint returns the exact outbound message body for provided test inputs And messages are encoded correctly for the target channel (GSM-7/UCS-2) without placeholder truncation
Quiet Hours Deferral
Given quiet hours [Qstart, Qend] are configured for the location and evaluated in the recipient’s local time And the calculated send time Ts falls within [Qstart, Qend] When dispatch time is evaluated Then the send is deferred to Qend if Qend is before or at the shift start time Else the send occurs at the shift start time And no message is transmitted during quiet hours
Opt-Out Compliance and Channel Substitution
Given the recipient has opted out of SMS (e.g., replied STOP) or the number is on a do-not-contact list When preparing the pre-shift dispatch Then no SMS is sent to that number And if a valid email address exists, the email variant is sent instead And the compliance decision (opt-out source, timestamp) is recorded And SMS messaging resumes only after START/UNSTOP consent is received and logged And initial SMS to new recipients includes compliant opt-out instructions
Shift Change Deduplication and Rescheduling
Given a pre-shift dispatch is pending or was sent for a specific shift occurrence And the shift is edited, swapped, called-in, reassigned, or canceled When the integration event is received Then any pending dispatch for the old schedule is canceled And any previously issued magic-link token for the old schedule is revoked And a new dispatch is scheduled per the configured offset and quiet-hours rules for the updated assignee/time And the assignee has at most one active magic link per shift occurrence
Magic Link Token Security and Device-Agnostic Landing
Given a dispatched magic link contains token T bound to staff S and shift H with expiration E When the link is first accessed before E Then a session is established and T is immediately marked as used (single-use) And subsequent accesses with T return an invalid/used response and do not start a session And accesses after E return an expired response with a request-new-link option And token entropy is at least 128 bits, signed server-side, and stored with E And the landing page renders without app install on current Safari, Chrome, Firefox, and Edge on iOS and Android
Event Logging and Deliverability Tracking
Given a pre-shift message is dispatched When delivery receipts, opens, or link clicks occur Then the system records events with ISO-8601 timestamp, staff ID, shift ID, and message ID And events are queryable in admin within 5 seconds of receipt and retained for at least 90 days And failed deliveries trigger up to 3 retries with exponential backoff starting at 30 seconds And a correlation ID links SMS/email, token, and all related events
Live Shift Change Handling
"As a manager, I want roster changes to update CurbPing automatically so that the correct staff have access without me sending messages."
Description

React in real time to swaps, call-ins, add-ons, and cancellations by consuming provider webhooks and recalculating who should receive or lose access to links. Revoke previously issued tokens when a shift is reassigned, issue new links to replacements, and suppress messages for canceled shifts. Maintain an audit trail of changes and support optional manager-approval rules.

Acceptance Criteria
Pre-Shift Swap Token Reassignment
Given an upcoming shift is assigned to Employee A and a pre-sent magic link was issued to A And a provider webhook with a valid signature indicates the shift is reassigned to Employee B before shift start When the webhook is received Then A’s tokens and any scheduled notifications for that shift are revoked within 10 seconds And no further shift notifications are sent to A And a new magic link is generated and delivered to B via SMS within 30 seconds And the shift’s access list shows only B as the assignee And an audit event is stored including old_assignee=A, new_assignee=B, provider_event_id, and timestamps
Mid-Shift Reassignment with Active Session
Given Employee A has an active CurbPing session for the shift And a provider webhook indicates an immediate swap to Employee B When the webhook is processed Then A’s session is terminated and A is auto-logged out within 15 seconds And A’s magic link becomes invalid and returns HTTP 401 on reuse And a new magic link is generated and delivered to B via SMS within 30 seconds And B can start a session on first click without manual provisioning And an audit event records termination_reason=swap, prior_session_duration, and reassigned_to=B
Call-In Add-On Assignment
Given a call-in/add-on adds Employee C to the active roster for a location and time When the provider add-on webhook is received Then a magic link is generated and sent to C via SMS within 30 seconds And C appears in the eligible staff list for that shift/location And existing assignees retain access and tokens remain valid And an audit event records action=add-on, assignee=C, delivery_message_id, and timestamps
Shift Cancellation Suppression
Given a future shift for Employee D has pending pre-send notifications or issued tokens And a provider webhook cancels the shift When the webhook is processed Then all pending/scheduled notifications for D for that shift are canceled And all issued tokens for that shift are revoked within 10 seconds And D receives no further messages about the canceled shift And an audit event records action=cancellation, tokens_revoked=true, provider_event_id, and timestamps
Manager Approval Gate for Swaps
Given manager-approval is enabled for roster changes at a location And a provider webhook proposes a swap from Employee E to Employee F When the webhook is received Then the change enters state=pending_approval and no tokens are revoked or issued And a manager approval request is sent with an actionable link via SMS/email And upon manager approval within 15 minutes the swap is applied and tokens/notifications are updated per standard flow And upon rejection or timeout the swap is not applied and involved parties are notified And all approval decisions are logged with approver_id, decision, and timestamps
Idempotent and Ordered Webhook Handling
Given the provider may deliver duplicate or out-of-order webhooks with shared event_ids or earlier timestamps When duplicate events are received Then processing is idempotent and no duplicate tokens or notifications are produced And events are applied in order by provider timestamp with a deterministic tie-breaker on event_id And the audit log shows a single consolidated state transition per logical change
Security and Audit Integrity for Revocations
Given a token is revoked due to a swap or cancellation When any user attempts to use the revoked link Then the system responds with HTTP 401 and a user-facing message to contact a manager And the attempt is logged with token_id, user_phone, ip_address, and timestamp And audit records are immutable and exportable as CSV/JSON with integrity checksums for the past 90 days
Clock-In Session Activation
"As an operator, I want sessions to begin at clock-in so that staff availability in CurbPing mirrors reality without manual tracking."
Description

Start a staff session automatically at clock-in based on scheduling or POS clock events; if unavailable, treat link activation as a soft clock-in. Support early/late clock-in tolerances, overlapping roles, and multi-location assignments. Ensure tokens bind to the active shift, and surface status to managers. Provide manual override to start/stop a session if needed.

Acceptance Criteria
POS Clock-In Auto-Activates Session
Given a confirmed rostered shift and a POS or scheduler clock-in event containing staff_id, shift_id, and location_id When the event is received within the configured early/late tolerance of the scheduled start Then activate a staff session within 10 seconds, set session.status="Active", and bind token to shift_id and location_id And display the staff as "On Duty" in Manager View with timestamp = event_time And ignore duplicate clock-in events for the same staff_id+shift_id (idempotent)
Magic Link Soft Clock-In Fallback
Given no clock-in event has been received for the staff's upcoming or current shift When the staff opens their magic link Then start a session with mode="Soft" and associate it to the nearest rostered shift within ±60 minutes; if none, create an unscheduled session tagged "Call-In" And upon later receipt of a matching clock-in event, convert mode to "Hard" and preserve the earlier of link_time and event_time as session.start_time And show a "Soft Clock-In" badge to managers until conversion occurs
Early/Late Clock-In Tolerance Handling
Given configuration early_tolerance_minutes and late_tolerance_minutes When a clock-in occurs earlier than scheduled_start by more than early_tolerance_minutes Then start the session immediately, set session.flag="Early", and display early_delta_minutes in Manager View and audit log When a clock-in occurs later than scheduled_start by more than late_tolerance_minutes Then start the session, set session.flag="Late", and display late_delta_minutes in Manager View and audit log When a clock-in occurs within tolerance Then start the session with no early/late flag
Overlapping Roles and Concurrent Shifts Resolution
Given multiple concurrent shifts or roles exist for the staff at clock-in time When a clock-in event arrives Then select the CurbPing-enabled shift with the highest configured role priority at the event location; if priorities tie, choose the shift whose scheduled_start is closest to event_time and not older than 60 minutes And bind the session token to the selected shift_id and role_id And if no unambiguous selection exists, set session.status="Needs Assignment" and notify the manager for resolution
Multi-Location Assignment Binding
Given a staff member is assigned to multiple locations When a clock-in event includes location_id Then bind the session to that location and restrict access to that location's queues and data When a soft clock-in occurs via magic link Then bind using the link's encoded location_id; if a later hard clock-in arrives from a different location, retain the original binding and flag "Location Mismatch" for manager review
Manager Visibility and Manual Override Controls
Given a manager is viewing the Manager View When any session starts (auto, soft, or manual) Then show the staff tile within 5 seconds with a status badge of Auto, Soft, or Manual and a live timer And provide controls to Start Session and Stop Session that take effect within 3 seconds and write an audit log with manager_id and reason When a manager starts a session manually for a staff without an active shift Then require selecting shift_id and location_id (or mark as Call-In) and set mode="Manual"
Auto-Logout at Shift End and Clock-Out Handling
Given configuration end_grace_minutes When a clock-out event is received for the active shift Then end the session within 5 seconds and set end_reason="Clock-Out" When scheduled_end + end_grace_minutes is reached without clock-out Then end the session automatically and set end_reason="Auto-Logout" When the schedule is extended before scheduled_end + end_grace_minutes Then extend the pending auto-logout to the updated scheduled_end + end_grace_minutes
Auto Logout at Shift End
"As a manager, I want staff to auto-logout at shift end so that rosters stay accurate and bays free up without manual policing."
Description

Automatically end sessions at scheduled shift end or on clock-out, with a configurable grace period and overtime extension rules. Protect active customer handoffs by delaying logout until tasks complete, and allow manager override for exceptions. Ensure logout revokes tokens, clears bay assignments, and updates availability indicators.

Acceptance Criteria
Auto logout at scheduled shift end with grace period
Given a staff member has an active session and no active handoff tasks And their shift end is 17:00 with a configured 10-minute grace period When the server clock reaches 17:10 Then the user is logged out within 5 seconds And all access tokens and magic links are revoked such that further API calls return 401 And any bay assignments held by the user are released and shown as available within 5 seconds And the user’s availability indicator changes to Off Shift in operator UI and API
Immediate logout on early clock-out
Given a staff member clocks out at 15:32 before their scheduled shift end And they have no active handoff tasks When the clock-out webhook is received Then the session is terminated within 5 seconds of receipt And all tokens are revoked and magic links no longer authenticate And bay assignments are cleared and availability is Off Shift
Protect active handoffs at shift end or clock-out
Given a staff member reaches shift end or submits clock-out And they have 1 or more active handoff tasks in progress When the grace period elapses Then logout is delayed until all active tasks are marked completed or transferred And if tasks are not completed within a configured max delay (e.g., 15 minutes), the system prompts for transfer and logs out after transfer or max delay is reached And upon logout, tokens are revoked, bays cleared, and availability set to Off Shift
Overtime extension rules apply post–shift end
Given a staff member reaches shift end with a configured overtime extension rule of up to 60 minutes And manager-approved overtime is present in the scheduling system When shift end passes Then the session remains active until the overtime end time or the cap is reached, whichever comes first And if overtime approval is absent, the normal grace-period logout applies And upon final logout, tokens are revoked, bays cleared, and availability set to Off Shift
Manager override to extend or force logout
Given a manager with appropriate permissions selects a staff member’s active session When the manager chooses Extend and sets a new end time Then the session remains active until that time unless earlier clock-out occurs And when the manager chooses Force Logout Then the session terminates within 5 seconds, revoking tokens, clearing bays, and setting availability to Off Shift And all overrides are recorded in an audit log with actor, target, action, timestamp, and reason
Shift swaps and call-ins adjust auto-logout timing
Given a scheduled shift is swapped from User A to User B effective 13:00 When the swap is received from the scheduling system Then User A’s session (if active) is set to auto-logout at 13:00 plus grace, unless earlier clock-out occurs And User B’s session, when started, will auto-logout based on User B’s new shift end And any call-in shift created for a user uses that shift’s end time for auto-logout
System-wide consistency on logout side effects
Given a user is auto-logged out by any trigger (shift end, clock-out, override) When logout occurs Then all API endpoints requiring authentication return 401 for old tokens within 5 seconds And the user is removed from any active bay assignment lists and queues within 5 seconds And the roster and availability feeds reflect Off Shift within 5 seconds And a logout event is emitted to webhooks/event bus with userId, trigger, timestamp, and correlationId
Attendance & Usage Visibility
"As an owner, I want clear attendance and usage reports so that I can measure adoption, coach staff, and verify ROI."
Description

Provide dashboards and exports that summarize attendance (on-time, late, no-show), session auto-start rate, link delivery/open/click rates, and per-location adoption. Enable filters by date, location, and role, and schedule daily/weekly email summaries. Retain event logs per retention policy while protecting PII and supporting audit needs.

Acceptance Criteria
Weekly Attendance Summary by Location
Given a manager selects a location and a Monday–Sunday date range When the Attendance dashboard loads Then it displays totals for scheduled shifts, on-time, late, and no-show with counts and percentages And on-time is defined as session start between scheduled start −5m and +5m And late is defined as session start > +5m and ≤ +30m after scheduled start And no‑show is defined as no session start recorded by +30m after scheduled start And calculations include approved swaps and call‑ins from the roster system And metrics reflect new clock‑in/clock‑out events within 60 seconds And values match a validated audit sample for the same filters with 0 variance And the dashboard renders in ≤2 seconds for up to 5,000 shifts in range
Usage Export with Link Delivery/Open/Click and Auto‑Start Rates
Given a user selects a date range, one or more locations, and roles When they request a Usage export Then CSV and XLSX files are available within 60 seconds And each file contains per-location daily rows with columns: date, location_id, scheduled_shifts, sessions_started, sessions_auto_started, auto_start_rate = sessions_auto_started / sessions_started (rounded to 1 decimal), magic_links_sent, delivered, opened, clicked, delivery_rate, open_rate, click_through_rate And exported timestamps use each location’s local time with time zone offset noted in the header And PII (phone/email) is masked by default; unmasked values require PII_View permission And exported counts equal the dashboard counts for the same filters
Role, Date, and Location Filters Apply Consistently
Given a user applies filters for date range, multiple locations, and roles When they navigate between Attendance and Usage views or trigger an export Then the same filters persist and are applied to all metrics and outputs And the URL reflects filter state for shareable deep links And first-load defaults are last 7 full days, the user’s home location(s), and all roles And zero-result states display an empty state with a Reset Filters action And changing filters updates visible metrics within 1 second
Scheduled Email Summaries to Owners
Given an owner schedules a daily or weekly summary at a specific local time and selects locations When the scheduled time occurs Then an email is delivered within ±10 minutes to all configured recipients And the email includes for each location: on-time/late/no-show counts and %, session auto-start rate, magic link delivery/open/click rates, and adoption rate And links in the email use expiring access tokens valid for 24 hours and require authentication for full detail And emails exclude PII and include a Manage Preferences link And hard bounces are retried up to 3 times and failures are logged and surfaced to admins
Event Log Retention and PII Protection
Given tenant retention_days is configured (e.g., 90) When an event exceeds retention_days Then identifying fields are purged or anonymized and the event is excluded from dashboards/exports except in aggregates And all event data is encrypted at rest (AES-256) and in transit (TLS 1.2+) And access to raw event logs requires Audit_View or PII_View permission; unauthorized access attempts are denied and audited And dashboards/exports mask PII by default (e.g., phone +1********34, email f*****@domain.com)
Audit-Ready Event Log Export and Access Traceability
Given an auditor with Audit_View permission selects a date range and location When they export event logs Then a CSV is produced with fields: event_id, event_type, timestamp (ISO 8601 UTC), actor_type, actor_id (pseudonymous), location_id, request_id, ip_hash, before_state, after_state And the export includes a signed SHA-256 checksum manifest for integrity verification And every export and dashboard view is logged with user_id, timestamp, and purpose-of-access note And the export honors retention_days and PII masking rules And exports of up to 1,000,000 events complete in ≤5 minutes
Per-Location Adoption Metric on Dashboard
Given a user selects a date range and locations When the Usage dashboard loads Then it displays an Adoption Rate per location defined as active_session_coverage = minutes with an active staff session during scheduled curbside coverage ÷ scheduled curbside coverage minutes And a location is flagged Adopted when active_session_coverage ≥ 85% with ≥ 5 shifts in period And shifts shorter than 30 minutes and scheduled breaks are excluded from the denominator And a tooltip explains the formula, numerator, and denominator for transparency And adoption values equal those in the Usage export for the same filters

Offline PIN

Resilient fallback when SMS or Wi‑Fi hiccups. Managers issue a rotating, location‑scoped PIN (or QR) to unlock a limited, audited session that expires quickly once connectivity returns. Keeps arrivals flowing during outages without exposing full permissions.

Requirements

Time-Scoped PIN/QR Generation
"As a store manager, I want to generate a rotating, location-scoped PIN or QR for outages so that staff can continue processing arrivals securely when connectivity drops."
Description

Provide managers the ability to generate cryptographically strong, rotating PINs and equivalent QR codes that are valid for short, configurable time windows and scoped to a specific location. Codes can be created on-demand or pre-scheduled, displayed in the manager console, and exported/printed for shift readiness. Each code includes metadata (issuer, creation time, validity window, max uses) and can be revoked at any time. The code format supports offline validation using locally cached public parameters so that staff devices can verify authenticity without network access.

Acceptance Criteria
Manager Generates Location-Scoped, Time-Boxed PIN/QR
Given I am a manager authenticated for location L And I specify a validity window (start, end) and max uses N When I generate a new Offline PIN/QR Then the system produces a unique PIN and an equivalent QR encoding the same signed payload And the payload includes issuer ID, code ID, location ID L, creation timestamp, validity window, and max uses N And the code is scoped to location L and is not valid at other locations And the code appears in the manager console with a countdown to expiry
Offline Authenticity and Validity Check on Staff Device
Given a staff device for location L has previously cached the public parameters for L And the device has no network connectivity When a user enters the PIN or scans the QR Then the device verifies the digital signature using the cached public parameters And confirms the current device time is within the validity window And confirms the location scope matches L And accepts the code if under max uses, otherwise rejects with an explicit message And completes validation locally in 500 ms or less And makes no network calls
Immediate Revocation by Manager
Given an active, unexpired code exists for location L When a manager revokes the code in the console Then the code status becomes revoked and it is removed from the active list And connected devices at L reject the code within 10 seconds of revocation And offline devices reject the code upon their next sync before accepting further uses And an audit event is recorded with actor, timestamp, reason, and affected code ID
Pre-Scheduled Rotation for Shift Readiness
Given I schedule a series of codes for a shift with defined time blocks and max uses When the start time of a block arrives Then the corresponding code auto-activates and becomes visible to staff And the prior block’s code auto-expires at its end time And I can edit or cancel any future scheduled block before activation And I can export a printable sheet containing each block’s PIN, QR, and metadata
Max Uses Enforcement Across Devices
Given a code is issued with max uses N When validations occur across one or more devices Then the system does not allow more than N successful validations in total And any attempt beyond N is rejected with a “Max uses reached” message And offline devices decrement a locally tracked remaining-uses count and reconcile on reconnect to prevent further accepts once N is reached
Export/Print with Reliable QR Scanning
Given I export codes for printing from the manager console When I generate a PDF export Then each entry includes PIN, QR, issuer, location, creation time, validity window, and max uses And the QR has sufficient error correction to scan reliably from a standard office print at 300 dpi And the QR and PIN are scannable/readable from the printed sheet under typical indoor lighting
Offline Session Sandbox
"As a curbside runner, I want a limited offline session unlocked by a PIN so that I can check in cars, assign bays, and mark handoffs without accessing sensitive settings."
Description

Enable a constrained, time-limited session that can be unlocked with a valid Offline PIN/QR, granting only essential capabilities: log customer arrival, assign bay, capture car details, and mark handoff. The sandbox excludes access to admin settings, reports, exports, and customer data beyond what is required for curbside flow. The session stores minimal data locally, uses device fingerprinting to bind the session to the unlocking device, and presents an “Offline Mode” UI with simplified forms optimized for poor connectivity.

Acceptance Criteria
Unlock Sandbox with Offline PIN During Outage
Given a staff user on Device X at Location A with no internet connectivity When a valid rotating Offline PIN scoped to Location A is entered on the Offline Mode entry screen Then a constrained offline sandbox session starts, is bound to Device X’s fingerprint, and the start time is stored locally Given a PIN that is expired or scoped to a different location When it is entered Then access is denied with an error stating the reason and no session is created Given repeated invalid PIN attempts When 3 consecutive invalid attempts occur within 5 minutes Then PIN entry is locked for 5 minutes and an audit event is queued locally Given a valid PIN with a TTL of 15 minutes When it is reused after TTL expiry Then access is denied and the attempt is audited locally
QR-Based Unlock in Offline Conditions
Given a staff user on Device X at Location A with no internet connectivity and camera permission granted When a valid location-scoped Offline QR is scanned Then a constrained offline sandbox session starts, is bound to Device X’s fingerprint, and the start time is stored locally Given an Offline QR that is invalid, expired, or scoped to a different location When it is scanned Then access is denied with a clear error and the attempt is audited locally Given camera permission is denied or camera fails When the user selects QR unlock Then the system provides a manual PIN entry fallback without leaving Offline Mode
Sandbox Capability Restrictions
Given an active offline sandbox session When the user opens the menu Then only core actions are available: Log Arrival, Assign Bay, Capture Car Details, Mark Handoff Given an active offline sandbox session When the user attempts to access Admin Settings, Reports, Exports, customer lists, or advanced configuration Then access is blocked with a message "Not available in Offline Mode" Given an active offline sandbox session When viewing customer information Then only data required for curbside flow (order code/reference and car details) is visible; phone, email, and order history are hidden
Minimal Local Data Storage and Protection
Given an active offline sandbox session When data is stored locally Then only the following fields are persisted: arrival_time, bay_number, car_make_model_color, license_plate (if provided), order_reference (hashed), action_type, staff_pseudonymous_id (hashed), device_fingerprint, location_id, local_timestamp Given local storage of offline records When inspected at rest Then records are encrypted and cannot be read without the app context Given successful sync or session termination When all local records are acknowledged by the server or the session expires Then local records are purged within 60 seconds and a purge audit is queued
Device Binding and Session Exclusivity
Given a sandbox session unlocked on Device X with fingerprint FX When the same PIN/QR is used on Device Y while the PIN/QR is still within TTL Then access is denied on Device Y and the attempt is audited locally Given a sandbox session unlocked on Device X When Device X’s fingerprint changes (e.g., storage cleared) during the session Then the session is invalidated and requires re-unlock Given multiple offline sessions exist for the same location When connectivity is restored and device binding is validated server-side Then conflicting sessions using the same credential are terminated, keeping the earliest valid session active
Connectivity Restoration and Auto-Expiry
Given an active offline sandbox session When stable connectivity is detected for at least 10 seconds and a server handshake succeeds Then the session auto-exits Offline Mode, syncs pending actions, and prompts the user to continue in online mode or logs out per policy Given an active offline sandbox session When 30 minutes have elapsed since session start or there is 5 minutes of inactivity (whichever comes first) Then the session expires, the user is logged out of Offline Mode, and re-unlock is required Given pending offline records When connectivity is restored Then 100% of records are synced with server acknowledgments; failed records retry with exponential backoff until success or explicit user logout
Offline Mode UI and Performance
Given an active offline sandbox session on a mid-range device When the Offline Mode UI loads Then the first interactive paint occurs within 2 seconds and the banner "Offline Mode" is visible at the top of the screen Given the user is on the Offline Mode home screen When navigating to Log Arrival, Assign Bay, Capture Car Details, or Mark Handoff Then each destination is reachable in 2 taps or fewer Given a user fills any Offline Mode form When submitting Then the action is committed locally within 200 ms and a queued status is shown; on failure to save, an inline error is shown with a retry option
Location Scope Enforcement
"As an area manager, I want Offline PINs to only work at their designated store so that access cannot be used at the wrong location."
Description

Enforce that Offline PINs/QRs only unlock sessions for their designated location. The location ID is embedded and signed within the code and validated locally against the device’s assigned store profile. If the device is not bound to the same location or the code is used outside its time window, the unlock is denied with clear error messaging. Optional geofence checks and manager override settings are supported when GPS is available.

Acceptance Criteria
Successful unlock for matching signed location
Given a device bound to location L and an Offline PIN/QR containing a valid signature and embedded location ID L within its time window When the user enters or scans the code while offline Then a limited offline session unlocks on the device And the session is explicitly scoped to location L And no cross-location views or actions are accessible
Deny unlock for location mismatch
Given a device bound to location A and a valid Offline PIN/QR containing a signed location ID B that is not equal to A When the user enters or scans the code Then the unlock is denied And an error message is displayed: "Code is for a different location" And the message includes the device location name and a masked code location identifier And no session state changes occur
Deny unlock outside code time window
Given a device bound to location L and an Offline PIN/QR for L whose start/end time does not include the current local time When the user enters or scans the code Then the unlock is denied And an error message is displayed: "Code not in valid time window" And the message shows the start and end times with the local timezone And an audit entry is recorded with reason "time_window_invalid"
Deny unlock for invalid signature or tampered payload
Given a device bound to location L and an Offline PIN/QR whose signature fails local verification or whose payload is malformed When the user attempts to unlock Then the unlock is denied And an error message is displayed: "Invalid or corrupted code" And an audit entry is recorded with reason "signature_invalid" And no session is created
Geofence enforcement when enabled and GPS available
Given a device bound to location L, an Offline PIN/QR for L, geofence enforcement enabled for L, GPS available with accuracy <= 50 meters, and a configured geofence radius R When the user attempts to unlock outside the geofence Then the unlock is denied And an error message is displayed: "Outside location geofence" including approximate distance from center And an audit entry is recorded with reason "geofence_outside" And when the device is inside the geofence, the unlock proceeds only if all other checks pass
Manager override on geofence failure scoped to location
Given geofence enforcement is enabled for location L, GPS is available, the device is outside the geofence, and manager override is permitted for L When an unlock is denied for reason "geofence_outside" and a manager provides a valid override credential within 60 seconds Then a one-time limited session unlocks scoped only to location L And the session expires in 10 minutes or when connectivity returns, whichever occurs first And an audit entry is recorded with reason "override_granted" including manager ID And no cross-location access is granted
Offline Action Audit & Sync
"As an owner, I want all offline activity to be captured and synced with an audit trail so that I can review what happened during outages."
Description

Record a tamper-evident audit log of all offline actions, including timestamps, device ID, code identifier, user (if known), and action details. Logs are stored securely on-device with sequence numbers and hashed integrity chains. Upon connectivity restoration, actions and logs are synced to the server with deduplication, conflict resolution (e.g., bay assignments), and reconciliation into the order timeline. Managers can review a dedicated “Outage Session” report with export options.

Acceptance Criteria
Offline Action Capture & Integrity Chain
- Given the device is offline and a valid Offline PIN session is active, when a supported offline action is performed (e.g., arrival check-in, bay assignment, mark ready/delivered, note), then a log entry is recorded containing: ISO8601 timestamp with timezone, device_id, session_id, code_identifier (PIN/QR), user_id if known, action_type, action_payload summary, and a sequential sequence_number starting at 1 for the session. - Given a new log entry is written, then its hash equals SHA-256(previous_entry_hash + canonicalized_entry_payload) and previous_entry_hash matches the prior entry’s hash; the first entry uses a fixed genesis value. - Given the app is force-closed or the OS reboots, when it restarts offline, then the session and existing log chain persist and the next entry uses sequence_number = last_sequence_number + 1. - Given on-device logs, then entries are stored encrypted at rest using the platform keystore and are not editable or deletable via the UI.
Offline Session Lifecycle and Expiry
- Given a rotating, location-scoped PIN/QR is entered while connectivity is degraded, when validated locally, then an offline session opens and a session_opened entry with session_id, location_id, start_timestamp is logged. - Given connectivity is continuously restored for at least 30 seconds, then the offline session auto-expires, a session_closed entry is logged, and further offline actions are blocked until re-authentication. - Given 10 minutes of inactivity during an offline session, then the session auto-expires and is logged. - Given an invalid or expired PIN/QR is entered, then session opening is rejected and a session_rejected entry with reason is logged.
Sync, Idempotency, and Retry
- Given connectivity is restored, when unsynced logs exist, then the client uploads batches in sequence order within 5 seconds, each action carrying idempotency_key = SHA-256(session_id + sequence_number) and previous_entry_hash. - Given the server receives actions with idempotency_keys already processed, then duplicate effects are not created and the response enumerates deduplicated items. - Given a 5xx or network failure during sync, then the client retries with exponential backoff (1s, 2s, 4s, up to 30s) for up to 10 attempts and preserves retry state across app restarts. - Given sync completes successfully, then local entries are marked synced and not resent, and the server returns the last_chain_hash which matches the client’s last entry hash.
Conflict Resolution and Reconciliation
- Given an offline bay assignment conflicts at sync time with an online assignment, then the server applies: if the requested bay is occupied at the action timestamp, assign the next available bay; else preserve the offline bay; a reconciled event is created and linked to the order. - Given two offline actions from different devices claim the same bay with overlapping times, then the server deterministically orders by (location_id, bay_number, action_timestamp, session_id, sequence_number), preserves the earliest, and reassigns the other to the next available bay; both actions are logged with reconciliation metadata. - Given merge into the order timeline, then offline actions appear in chronological order, use sequence_number to break ties, are labeled Offline (synced), and include reconciliation notes where applicable.
Manager Outage Session Report and Export
- Given a manager opens the Outage Sessions report for a date range and location, then sessions are listed with: session_id, device_id, location, start/end timestamps, PIN/QR identifier, user (if known), action_count, conflicts_count, integrity_status, and export options. - Given Export CSV is selected, then a CSV downloads with one row per log entry containing all captured fields plus reconciliation results; encoding is UTF-8 and timestamps are ISO8601 with timezone. - Given Export PDF is selected, then a paginated PDF renders with a per-session summary and a chronological appendix of actions; hashes may be truncated to 8 characters for readability without losing linkage. - Given a session with a broken hash chain, then the report highlights it as Compromised and filtering supports include/exclude compromised sessions.
Data Retention, Storage Limits, and Privacy
- Given on-device log storage exceeds 50 MB or oldest unsynced logs exceed 7 days, then the app blocks opening new offline sessions and prompts the user to sync or export; unsynced logs are not auto-deleted. - Given logs have been successfully synced and are older than 30 days, then the app purges those logs from the device while retaining session headers; the server retains full records per policy. - Given PII fields exist, then offline payloads minimize PII (e.g., masked phone, partial order reference) and are encrypted at rest; reports reveal PII only to authorized roles. - Given a manager with proper permission invokes Clear Offline Data, then all synced logs are securely deleted from device storage with user confirmation; unsynced data remains until synced or exported.
Auto-Expiry and Re-Auth on Recovery
"As a manager, I want offline access to expire immediately once service is restored so that normal security policies resume."
Description

Automatically expire offline sessions when connectivity is restored or when the configured maximum offline duration is reached, whichever comes first. Detect network/API recovery and SMS gateway health, display a countdown and warnings, and require full re-authentication to resume normal operations. Support manager-initiated manual revocation and single-use/limited-use codes to prevent sharing or reuse.

Acceptance Criteria
Connectivity Recovery Auto-Expiry
Given an active offline session established via a valid location-scoped Offline PIN and the client is in offline mode And heartbeatInterval is configured (default 5s) and recoveryStableChecks is configured (default 2) When the client records recoveryStableChecks consecutive successful reachability checks to the API within one heartbeatInterval each Or when the SMS gateway health endpoint returns green for recoveryStableChecks consecutive checks Then the offline session expires within 2 seconds of the qualifying recovery event And the user is redirected to the re-authentication screen with a banner "Connection restored — sign in to continue" And all offline actions (arrival check-in, bay assignment, order updates) are disabled And an audit log entry is recorded with reason "recoveredConnectivity", including timestamps, deviceId, userId (if known), locationId, and sessionId
Max Offline Duration Auto-Expiry
Given an active offline session with maxOfflineMinutes configured (e.g., 15 minutes) When the elapsed offline time reaches maxOfflineMinutes Then the session expires within 2 seconds And the user is redirected to the re-authentication screen with a banner "Offline session expired — time limit reached" And offline actions are disabled immediately And an audit log entry is recorded with reason "maxDuration", including timestamps, deviceId, locationId, configuredLimit, and sessionId
Offline Session Countdown and Warnings
Given an active offline session with a known expiryTime and warning thresholds configured at T-5m and T-1m When the session starts Then a persistent countdown timer is displayed on all offline screens, updating at least once per second And at T-5m, a non-blocking warning banner appears And at T-1m, the banner escalates to a blocking modal requiring acknowledgment And the countdown and warnings are accessible (screen-reader readable, 4.5:1 contrast, not solely color-dependent) And if connectivity is restored before expiry, the countdown stops and the session expires as per recovery rules
Forced Full Re-Authentication After Expiry
Given an offline session that has expired due to either connectivity recovery or max duration When the user attempts any protected action Then a full re-authentication flow is required (username/password or SSO), and Offline PIN is not accepted And upon successful re-auth, the user is returned to the last intended screen in online mode And if re-auth fails, no offline actions are permitted And the expired offline token is invalidated across all tabs on the device And an audit log entry is recorded with reason "reauthRequired", including attempt outcome and session linkage
Manager Manual Revocation
Given a manager with permission to revoke offline sessions for a specific location And an active offline session exists for that location When the manager revokes the session from the dashboard (or via manager QR revoke flow) Then the client checks revocation status every heartbeatInterval while offline and immediately upon any connectivity event And upon the next revocation check that returns the revoked status, the client expires the session within 2 seconds And the user is redirected to re-auth with a banner "Session revoked by manager" And the audit log contains paired entries on server and client with reason "managerRevoked" including managerId, sessionId, locationId, and timestamps
Single-Use and Limited-Use Code Enforcement
Given an Offline PIN/QR configured as single-use for location A When the first device activates an offline session successfully Then any subsequent activation attempts with the same code (by any device) are rejected with error "Code already used" And an audit log entry is recorded for each rejected attempt with reason "codeUsed" and device fingerprint Given an Offline PIN/QR configured as limited-use with maxUses=M for location A When M distinct devices activate within the validity window Then the (M+1)th activation attempt is rejected with error "Use limit reached" And only devices counted toward M may hold active sessions concurrently And the code cannot be used at any location other than A (attempts are rejected with reason "wrongLocation")
Outage Detection & Fallback UX
"As a staff member, I want clear prompts when the system goes offline and guidance to use the PIN/QR so that I can keep the line moving."
Description

Detect outage conditions such as unreachable APIs, failed WebSocket heartbeats, or SMS delivery suspensions and automatically prompt staff with a clear Offline Mode banner and step-by-step fallback instructions. Pre-cache critical assets (UI shell, forms, code validators) for offline use, provide concise guidance for scanning a QR or entering a PIN, and include accessibility-friendly, high-contrast screens optimized for outdoor glare and gloves.

Acceptance Criteria
Automatic Offline Mode Trigger on Connectivity Failures
Given the staff console is operating in normal mode When any of the following occurs within 60 seconds: 3 consecutive API requests result in network error, timeout (>10s), or HTTP 5xx; 3 consecutive missed WebSocket heartbeats at 10-second intervals; or an SMS provider webhook indicates account-level delivery suspension Then an "Offline Mode" banner is displayed within 2 seconds including a reason code and timestamp And an outage-start event is written to the local audit log with type and details
Offline PIN or QR Fallback Activation
Given the "Offline Mode" banner is visible When a staff user selects "Use Offline PIN/QR" Then the app presents both a PIN entry field and a QR scan option labeled with the current location name And entering a valid rotating, location-scoped PIN issued within the last 15 minutes or scanning a valid QR token starts a limited offline session And invalid or expired credentials display an error and do not start a session And the limited session exposes only Arrival Check-in and Bay Assignment; Settings, Reports, and Staff Management remain inaccessible
Pre-cached UI Shell, Forms, and Validators Load Offline
Given the service worker has been installed and initial sync has completed When the device is offline and the staff console is opened Then the UI shell, Arrival Check-in form, Bay Assignment view, and client-side validators load from cache and become interactive within 2 seconds on a mid-tier device (P75) And no network requests are attempted for these assets while offline And a versioned cache manifest lists these assets and passes an integrity check
Accessible, High-Contrast Offline Screens for Outdoor Use
Given any Offline Mode banner or screen is displayed Then all text and interactive elements have a contrast ratio >= 7:1 And all tappable targets are at least 44x44 CSS pixels with at least 8px spacing And the flow is fully operable via keyboard and screen readers; all controls have programmatic labels and roles And critical actions require at most one tap/click; no multi-touch gestures are required
Automatic Recovery and Expiry of Offline Session
Given a limited offline session is active When connectivity is restored and the app records 3 consecutive successful API pings and 3 WebSocket heartbeats within 30 seconds Then the app exits Offline Mode and revokes offline privileges within 5 seconds And queued offline actions are synced exactly once and marked uploaded in the audit log And the user is returned to normal mode without a page refresh
Audited Offline Session and Actions
Given any offline session starts or ends Then the audit log records user ID, location ID, device fingerprint, start and end timestamps, outage reason code, credential type (PIN or QR; hashed), and a list of actions performed And audit records are stored locally with encryption while offline and uploaded within 60 seconds after recovery And if upload fails, records remain queued and are retried with exponential backoff for up to 7 days
Clear Fallback Instructions in Offline Mode
Given Offline Mode is active Then a visible step-by-step guide (no more than 3 steps) explains how to scan a QR or enter a PIN and where to obtain the PIN And the instructional text reads at or below Flesch-Kincaid Grade 8 and is available in en-US and es-US And the guide includes large icons and high-contrast illustrations suitable for outdoor glare And the instructions are dismissible only after a valid session starts or the user exits Offline Mode
Manager Console for PIN Distribution
"As a store manager, I want to schedule and distribute Offline PINs for upcoming shifts so that we are ready if connectivity dips."
Description

Add console tools for creating, scheduling, and distributing Offline PIN/QR codes by shift. Allow configuration of validity windows, maximum uses, rotation frequency, and revocation. Provide printable signage with QR and numeric fallback, delivery via SMS/email to staff, and an audit trail showing who generated, viewed, or revoked each code. Include policy presets (e.g., short outage vs. full ISP outage) for quick setup.

Acceptance Criteria
Create and Schedule Offline PIN by Shift
Given I am a Manager scoped to Location X, When I open Manager Console > Offline PIN and select "New Code", Then I can set location scope (Location X), shift, validity start/end (with timezone), max uses, and rotation frequency. Given required fields are missing or invalid (end before start, max uses < 1), When I click Save, Then the form shows inline errors and does not save. Given the start time is in the future, When I save, Then the code status is "Scheduled" and no PIN/QR is issued until start. Given current time reaches the start within the validity window, When the schedule triggers, Then the system generates a unique PIN and QR and marks the code "Active". Given current time is past the end, When accessed, Then the code is "Expired" and cannot be used.
Configure PIN Rotation Frequency and Automatic Code Rotation
Given rotation frequency is set to N minutes, When an active code reaches N minutes of age within the validity window, Then a new PIN/QR is generated and the prior code is marked "Superseded" and blocked for new sessions. Given a grace period G minutes is configured, When rotation occurs, Then existing sessions started with the prior code continue for up to G minutes or until connectivity returns, whichever is sooner. Given rotation is disabled, When time elapses within the validity window, Then the PIN/QR remains unchanged until expiry or revocation.
Revoke Active PIN/QR and Block New Sessions
Given a code is Active, When I click Revoke and provide a reason, Then the code becomes Invalid immediately and its QR/PIN returns an error message on scan/entry. Given a code is Scheduled, When I click Revoke, Then the code is canceled and will not activate at start time. Given revocation occurs, Then an audit entry is recorded with timestamp, actor, location, reason, and code identifier.
Distribute Codes to Staff via SMS/Email with Delivery Tracking
Given I select staff for Location X and choose SMS and/or email, When I click Send, Then recipients receive a message containing the QR link and numeric PIN and the console shows per-recipient delivery status (Sent/Delivered/Failed). Given a delivery fails, When the system receives a failure callback or times out, Then the console flags the failure and offers retry and copy-to-clipboard options. Given a staff member is not scoped to Location X, When attempting to send, Then the console prevents selection and indicates scope mismatch.
Generate Printable Signage with QR and Numeric Fallback
Given a code is Scheduled or Active, When I click Generate Printable, Then a PDF is produced with QR, numeric PIN, location name, validity window, and brief instructions, formatted for Letter/A4 with high-contrast design. Given the PDF QR is scanned using default iOS/Android camera, When scanned at typical arm's-length under indoor lighting, Then it resolves to the correct code URL within 2 seconds. Given the numeric PIN is entered on the arrival page, When the code is Active, Then the arrival flow unlocks; When the code is Expired or Revoked, Then an error explains the status.
Audit Trail for Code Lifecycle and Access
Given any of the following events occur: create, edit, view code, download signage, send via SMS/email, rotate, revoke, expire, When I open Audit Trail, Then an immutable entry shows timestamp (UTC), actor, location, event type, target code id, and details (e.g., recipients count, reason). Given I apply filters (date range, location, actor, event type), When I click Apply, Then the list updates accordingly and can be exported to CSV. Given a user lacks permission for a location, When they attempt to view its audit entries, Then access is denied.
Apply Policy Presets for Outage Scenarios
Given I select the "Short Outage" preset, When applied, Then the form pre-fills validity window (e.g., 1 hour), rotation frequency (e.g., 10 minutes), max uses (e.g., 25), and grace behavior defined by the preset, and shows a summary before save. Given I select the "Full ISP Outage" preset, When applied, Then the form pre-fills a longer validity window (e.g., 8 hours), rotation frequency (e.g., 30 minutes), and distribution options (printable signage + staff SMS), and I can override any field prior to saving. Given I save with a preset applied, When the schedule is created, Then the selected preset name is stored with the code for reporting and audit.

Tap‑to‑Elevate

Frontline staff can request temporary higher access (e.g., refunds via Instant Adjust) and a supervisor approves with a quick magic‑link tap. Elevation is time‑bound and action‑scoped, then auto‑reverts—ending account sharing while resolving guest issues faster.

Requirements

One-Tap Elevation Request
"As a frontline staff member, I want to request temporary permission for a specific action on an order so that I can resolve a guest issue without sharing logins or waiting on lengthy approvals."
Description

Enable frontline staff to initiate a temporary access elevation tied to a specific order and action (e.g., refund up to a set limit or order reassignment) by submitting reason, scope, and suggested duration from mobile web or POS. Requests are validated, captured with user context, and routed to available supervisors, optimized for SMS-first workflows and low-friction initiation.

Acceptance Criteria
Mobile Web/POS One‑Tap Request Submission
Given a frontline staff member is viewing an eligible order on mobile web or POS, When they tap "Request Elevation", Then a prefilled form displays order ID, permitted action scopes (e.g., Refund, Reassign), default duration of 15 minutes, and a required reason field. Given the staff enters a reason and selects scope and duration, When they tap "Submit", Then a request with a unique ID and status "Pending Approval" is created server‑side and acknowledged to the client within 2 seconds. Given submission succeeds, Then the UI confirms with a visible status chip showing "Pending Approval" without page reload and the total interaction requires ≤2 taps plus one text entry. Given the device is offline at submit time, When connectivity resumes within 60 seconds, Then the request auto‑retries once and surfaces success or a clear failure reason.
Input Validation and Policy Limits
Given the elevation form is open, When the user attempts to submit with a blank reason, Then an inline error appears and submission is blocked until a reason ≥ 8 characters is provided. Given a duration is selected, When it is < 5 minutes or > 60 minutes, Then submission is blocked with an inline error and the field is clamped to the nearest allowed value. Given a Refund scope is selected, When the requested amount exceeds the configured refund cap or the order total, Then submission is blocked and the cap value is displayed. Given the same user submits an identical request for the same order within 30 seconds, Then the system returns the existing pending request ID instead of creating a duplicate and surfaces a "Request already pending" message. Given the order is closed, canceled, or not eligible, When submission is attempted, Then the system rejects with a specific reason code and no request is created.
Supervisor Routing via SMS Magic‑Link
Given a request enters "Pending Approval", When routing runs, Then at least one on‑duty supervisor for the location is selected using least‑recently‑notified logic and an SMS with a single‑tap magic‑link is sent within 10 seconds. Given no supervisors are on duty, When routing runs, Then the requester is notified immediately with "No supervisors available" and the request is placed in "Queued" for up to 15 minutes, with routing retries every 2 minutes; after 15 minutes the request auto‑expires with reason recorded. Given the SMS is sent, Then it includes order ID, requester name, location, requested scope, proposed duration, and request ID, and no login is required to act on the link. Given an SMS delivery failure occurs, Then the event is logged and routing automatically retries to the next available supervisor within 30 seconds.
Supervisor One‑Tap Approve/Deny
Given a supervisor opens the magic‑link, When they tap Approve, Then the request status changes to "Approved" and elevation is granted to the requester within 3 seconds. Given a supervisor opens the magic‑link, When they tap Deny, Then the request status changes to "Denied" with an optional denial reason captured. Given an approval or denial occurs, Then the requester receives confirmation via in‑app banner (POS/mobile web) and SMS within 5 seconds. Given a magic‑link is used once or 10 minutes elapse since issuance, When it is accessed again, Then the page shows "Link expired/used" and no state changes occur. Given multiple supervisors act on the same request, When the first action is recorded, Then subsequent actions are rejected as duplicates and logged.
Time‑Bound Auto‑Revert and Revocation
Given an elevation is approved with a defined duration, When the duration elapses, Then the elevation auto‑reverts within 5 seconds and the requester is notified. Given an elevation is approved for a specific action (e.g., Refund), When the action is completed successfully, Then the elevation auto‑reverts immediately (≤5 seconds) regardless of remaining time. Given a supervisor selects Revoke from the approval screen, When invoked before expiry, Then the elevation is removed immediately and the requester’s subsequent attempts are blocked with a clear message. Given the requester takes no action, When 10 minutes pass after approval, Then the elevation auto‑revokes due to inactivity and is logged with reason "Unused".
Action‑Scoped Permission Enforcement
Given an elevation is active for Refund up to $X, When the requester attempts a refund > $X, Then the operation is blocked with a clear cap message and logged. Given an elevation is active for Order Reassignment on order O, When the requester attempts to reassign any other order, Then the operation is blocked with 403/"Not within approved scope". Given an elevation is active, When the requester attempts unrelated admin operations (e.g., user management, global settings), Then access is denied and no data is altered. Given an elevation is active, When actions involve another location, Then cross‑location operations are denied unless explicitly included in scope.
Audit Trail, Observability, and Retrieval
Given any request lifecycle event (create, validate error, route, approve/deny, grant, action performed, revoke, expire) occurs, Then an immutable audit log entry is written with timestamp, request ID, order ID, requester ID, supervisor ID (if applicable), channel (mobile web/POS/SMS), and IP/user‑agent. Given an admin searches Admin > Audit by request ID or order ID, When the query is executed, Then results return within 2 seconds and include the full event timeline. Given an export is requested for a date range, When generated, Then a CSV is downloadable within 30 seconds containing all fields above plus delivery status for supervisor notifications. Given retention policy is applied, Then audit records are retained for ≥ 180 days and PII fields are masked where configured, without loss of critical traceability.
Magic-Link Approval & Denial
"As a shift supervisor, I want to approve or deny elevation via a single secure tap so that I can unblock staff quickly while maintaining control and security."
Description

Deliver supervisor approval prompts via secure single-use magic links sent over SMS/email that open a lightweight approval screen with request details. Support approve, deny, or modify (shorten duration, tighten scope, set caps). Links are time-limited, device-bound, and replay-protected, with optional MFA/PIN confirmation for high-risk scopes to keep approval one-tap fast yet secure.

Acceptance Criteria
SMS/Email Delivery of Secure Magic Link
Given a valid Tap‑to‑Elevate request with a designated supervisor contact When the system generates an approval prompt Then an SMS and/or email containing a single‑use magic link is delivered to the contact within 5 seconds p95 And the link token has at least 128 bits of entropy and contains no PII And the message includes requester name, location, scope summary, requested duration, and request ID And delivery failures trigger one automatic retry for SMS and fallback to email within 30 seconds And delivery events (sent, delivered, failed) are recorded with timestamps
One‑Tap Approval Grants Time‑Bound, Action‑Scoped Elevation
Given an unexpired magic link opened on the first device When the supervisor taps Approve Then the approval screen displays requester identity, role, location, requested scope, requested duration, monetary cap (if any), and request ID And the requester’s elevation activates within 2 seconds p95 And the elevation is limited to the requested scope and duration And the requester and supervisor receive confirmation within 2 seconds p95 And the link is consumed and cannot be used again And the elevation auto‑reverts exactly at expiry with no further action
One‑Tap Denial Blocks Elevation and Notifies Requester
Given an unexpired magic link When the supervisor taps Deny and optionally enters a reason Then no elevation is granted And the requester is notified within 2 seconds p95 including the denial reason if provided And the link is consumed and cannot be reused And the denial is logged with supervisor identity and device fingerprint
Supervisor Modifies Scope/Duration/Cap Before Approval
Given an unexpired magic link and a request proposing scope, duration, and optional monetary cap When the supervisor edits the request Then only tightening changes are permitted (narrower scope, shorter duration, lower cap) And invalid expansions are blocked with inline validation messaging And upon Approve, the elevation reflects the modified values And the modified values are shown in the confirmation to both parties And the modification details are captured in the audit log
Single‑Use, Time‑Limited, Device‑Bound, Replay‑Protected Link
Given a magic link with a 10‑minute TTL When it is first opened on Device A Then subsequent opens on Device B are blocked with a device‑bound message and no state change And any second attempt on Device A after consumption shows “already used” with no state change And any open attempt after TTL shows “link expired” with a re‑request option And API calls include a nonce that is validated server‑side to prevent replay And all blocked attempts are logged without changing request status
High‑Risk Scopes Require MFA/PIN Confirmation
Given a request classified as high‑risk by policy (e.g., refund over $100) When the supervisor taps Approve Then a 6‑digit PIN or MFA challenge is required before approval completes And up to 3 attempts are allowed before the link is locked And successful MFA completes within 10 seconds p95 and then activates elevation And failed or locked MFA does not grant elevation and is logged
Complete Audit Trail and Real‑Time Notifications
Given any approve, deny, or modify action When the action is taken Then an audit record is stored containing request ID, action, before/after values, supervisor identity, device fingerprint, IP, timestamp, delivery channel, and MFA status And records are immutable and queryable by request ID within 1 second And the requester’s device receives a real‑time update within 2 seconds p95 And webhooks (if configured) emit the action within 2 seconds p95 with signature verification
Time-Bound, Action-Scoped RBAC
"As an operator, I want elevated access to be limited by time and scope so that we minimize risk and prevent misuse while enabling fast problem resolution."
Description

Issue temporary tokens on approval that enforce least-privilege access for only the approved actions, resources, limits, and time window (e.g., 10 minutes, refund up to $X on Order #123). Automatically revoke on expiry or completion, prevent lateral access, and block persistence beyond scope. No permanent role changes; all enforcement occurs at the API and UI control levels.

Acceptance Criteria
Issue and Enforce Time‑Bound, Order‑Scoped Refund Token
Given a frontline user requests a refund on Order #123 for $25 and a supervisor approves via magic link When the system issues a temporary token valid for 10 minutes scoped to refund up to $25 on Order #123 Then the UI renders refund controls only on Order #123 and not on any other orders And the API permits a single refund call for Order #123 up to $25 and rejects >$25 with 403 RBAC_LIMIT_EXCEEDED And the token includes an absolute expiry timestamp and server time is the source of truth And no other elevated actions (e.g., edit menu, adjust bay assignments) become available in UI or API
Auto‑Revocation on Token Expiry (Time Window)
Given a temporary elevation token valid for 10 minutes starting at approval When 10 minutes have elapsed since approval Then the UI immediately hides or disables all elevated controls without page refresh And subsequent API calls using the token are denied with 403 RBAC_TOKEN_EXPIRED And reloading the page or opening a new tab does not restore elevation And background or queued requests initiated before expiry do not execute elevated actions after expiry
Auto‑Revocation on Action Completion
Given a temporary elevation token scoped to a single refund on Order #123 When the refund on Order #123 is executed successfully Then the token is immediately invalidated and cannot be reused And repeat refund attempts via UI or API are denied with 403 RBAC_SCOPE_SPENT And the UI confirms completion and removes elevated controls for that order And no additional orders can be refunded under the spent token
Prevent Lateral Access During Elevation
Given a token scoped to refund up to $25 on Order #123 only When the user attempts to refund Order #124 or any other order Then the API denies the request with 403 RBAC_RESOURCE_MISMATCH and the UI shows no refund controls for those orders And attempts to invoke other elevated functions (e.g., issue comp, edit menu, adjust bays) are denied in UI and API And attempts to list or view restricted resources outside baseline role are blocked per baseline permissions And URL manipulation or direct API calls cannot expand the token scope
No Permanent Role Changes or Persistence Beyond Scope
Given a user receives a temporary elevation token When the token expires or is spent Then the user’s baseline role and permissions remain unchanged in the directory/role store And logging out/in or starting a new session does not restore elevation without a new approval And bookmarks, stored URLs, or local/session storage do not retain any elevated capability And organization/user role assignments show no updates attributable to the elevation
API‑Level Enforcement with Consistent UI Controls
Given constraints encoded in the temporary token (actions, resources, limits, expiry) When any client (browser, API client, curl) invokes endpoints with the user’s auth Then the API enforces the constraints independently of the UI and denies out‑of‑scope requests And the UI does not render controls for actions the API would deny based on the token And mismatches between UI state and API constraints result in no state change server‑side And errors returned by the API include specific RBAC error codes for testability and observability
Instant Adjust Refund Integration
"As a cashier handling a refund, I want elevation to unlock Instant Adjust in the order so that I can issue the correct refund immediately with proper limits and documentation."
Description

Integrate Tap‑to‑Elevate with Instant Adjust so that approved elevations unlock refund workflows within the order context. Pre-fill amounts and caps from the approval, require reason codes, block duplicates, and ensure receipts and settlement records attribute actions to the elevated user and approver. Handle partial and full refunds and reflect status in guest communications.

Acceptance Criteria
Refund Workflow Unlocks on Approved Elevation
Given a frontline user has an approved Tap-to-Elevate for "Refund" on Order <order_id> When the user opens the order context during the active elevation window Then the Instant Adjust refund workflow is visible and enabled for that user And the refund workflow remains hidden or disabled for users without an approved elevation on the same order And the refund capability is scoped only to the approved order and action ("Refund")
Amount Pre-Fill and Cap Enforcement from Approval
Given an approved elevation includes an approved amount and a maximum refund cap When the user opens the refund form in the order context Then the amount field is pre-filled to the approved amount And the maximum enterable amount is limited to the lesser of the approval cap and the order’s refundable balance And attempts to submit an amount above that limit are blocked with a clear validation message and disabled submit
Reason Code Required for Refund Submission
Given the refund form is open When the user attempts to submit without selecting a reason code from the configured list Then the submit action is prevented and an inline error indicates a reason code is required When the user selects a valid reason code Then the error clears and submission is allowed
Duplicate Refund Attempts Are Prevented
Given a refund for Order <order_id> with amount <amount> has been successfully processed When any user attempts to submit an identical refund for the same order and amount Then the system rejects the attempt as a duplicate and no additional refund record is created And the user receives a clear message that the refund already exists
Receipts, Settlement, and Audit Attribution
Given a refund is completed under Tap-to-Elevate When guest and merchant receipts and settlement exports are generated Then the refund action is attributed to the elevated user and the approver by name/ID and timestamp And the elevation approval identifier is recorded in the audit log and is retrievable via reporting
Partial and Full Refund Handling in Order Context
Given an order with a refundable balance When the user submits a partial refund less than the remaining balance Then the system creates a partial refund record, reduces the refundable balance accordingly, and leaves the order open for additional eligible refunds When the user submits a refund equal to the remaining balance Then the system creates a full refund, sets the refundable balance to zero, and prevents further refunds
Guest Communications Reflect Refund Status
Given SMS is the guest communication channel When a refund is completed successfully Then the guest receives an SMS confirming the refund amount, order identifier, and expected settlement time frame When a refund fails or is reversed Then the guest receives an SMS explaining the failure status and next steps, and no duplicate success message is sent
Comprehensive Audit Trail & Reporting
"As an owner, I want a detailed audit trail and reports for elevated actions so that we have accountability, can investigate issues, and meet compliance requirements."
Description

Record immutable logs for each elevation request and all actions taken under it, including requester, approver, scope, timestamps, order IDs, device/IP, outcomes, and denials. Provide export, webhooks, and dashboard filters (by store, user, action, time, amount) with retention and anomaly alerts (e.g., multiple high-value elevations per shift).

Acceptance Criteria
Immutable Elevation Request Log Capture
Given a staff member submits a Tap‑to‑Elevate request When the request is created Then the system stores an immutable log containing elevation_id, requester_user_id, requester_role, store_id, requested_scope, requested_at (ISO 8601 UTC), requester_device_fingerprint, requester_ip, requester_user_agent, and optional order_id Given a supervisor approves or denies the request When the decision is recorded Then the log receives an append-only version with approver_user_id, decision (approved|denied), decision_reason (required on denial), decided_at (UTC), and expires_at (if approved), without overwriting prior fields Given any user or admin attempts to modify or delete an existing log record When the API or database is called to update or delete Then the operation is rejected with 403 and a security_event is logged with actor_user_id, attempted_operation, and timestamp
Action Logging Under Elevated Session
Given an elevation is approved When a privileged action (e.g., Instant Adjust refund) is performed under that elevation Then the system logs action_id, elevation_id, actor_user_id, action_type, order_id, amount and currency (if applicable), outcome (success|failure), action_timestamp (UTC), device_fingerprint, ip, and store_id Given multiple actions occur under the same elevation When the elevation detail is retrieved Then all actions are listed in chronological order and the response includes totals for count and refunded_amount Given an action is attempted outside the approved scope When the action is invoked Then the action is blocked with 403, no state change occurs, and a denied_action event is logged with reason "out_of_scope"
Time-Bound Elevation Auto-Revert and Enforcement
Given an approved elevation with an expires_at When expires_at is reached Then the elevation status changes to expired automatically within 60 seconds and further privileged actions require a new approval Given a user attempts a privileged action after expiration When the attempt occurs Then the system denies the action with 403 and logs an expired_elevation_denial including attempted_action, user_id, and timestamp Given a supervisor revokes an elevation before expires_at When revocation is submitted Then the elevation status is set to revoked immediately, subsequent privileged actions are blocked, and revocation is logged with revoker_user_id and reason
Dashboard Filtering, Sorting, and Pagination
Given the audit dashboard is open When filters by store_id, user_id, action_type, time range (UTC), and amount range are applied (individually or combined) Then only matching records are displayed Given sorting by timestamp desc and pagination size 50 When results are requested on a dataset of ≥1,000,000 records Then the first page renders in under 2 seconds at p95 and subsequent pages in under 2 seconds at p95 Given pagination controls are used When moving to next or previous pages Then results are consistent using a stable cursor and the total count reflects the filtered dataset Given a filtered/sorted view is active When a shareable URL is copied and opened in a new session Then the same filters and sorting reproduce the same result set
Exports (CSV/JSON) and Webhook Delivery
Given a filtered set of audit records When an export is requested Then downloadable CSV and JSON are generated with headers, UTC timestamps, comma-safe quoting, and include elevation_id and action_id Given the export exceeds 50,000 rows When the export is requested Then the system streams the file or emails a secure link within 10 minutes, and the export job status is trackable until completion Given audit events occur (request_created, approved, denied, action_performed, elevation_expired, revoked, denied_action) When webhooks are configured Then each event is POSTed within 30 seconds with HMAC-SHA256 signature, idempotency key, and retried with exponential backoff for up to 24 hours until a 2xx response
Retention Policy Configuration and Enforcement
Given organization retention_days is set to N (N ≥ 90) When the daily retention job runs Then audit records older than N days are permanently purged and a retention_purge summary (counts by event type and timestamp) is written to the system audit Given retention_days is updated When the setting is saved Then the change is audit-logged with editor_user_id, old_value, new_value, and timestamp, and the new value applies to subsequent purge runs Given a request is made for data outside the retention window When exporting, querying, or delivering webhooks Then no purged records are returned or sent
Anomaly Detection and Alerts for High-Value Elevations
Given anomaly detection is enabled When a user completes ≥2 approved elevations each resulting in refunds totaling > $200 within any 8-hour shift at the same store Then an anomaly alert is generated within 5 minutes including user_id, store_id, elevation_ids, total_amount, and time window Given an anomaly alert is generated When alert destinations (email, SMS, webhook) are configured Then notifications are delivered to all configured channels as a single deduplicated incident per user per store per shift Given an anomaly alert exists When viewed in the dashboard Then it links to the underlying audit records and the alert itself is recorded in the audit log
Escalation & Fallback Pathways
"As an on-duty manager, I want a safe fallback when a supervisor isn’t available so that guest issues are resolved without stalling service or compromising security."
Description

If no supervisor responds within a defined SLA, automatically escalate to backup approvers and offer a break-glass path requiring stronger authentication and justification, generating high-severity alerts. Provide offline fallback codes if SMS delivery fails, with tight scopes and immediate post-use revocation and review.

Acceptance Criteria
Primary Supervisor Non‑Response Triggers Tier‑1 Escalation
Given a Tap‑to‑Elevate request awaiting supervisor approval and a store SLA of 60s When the primary supervisor has not approved or rejected within 60s Then the system notifies the Tier‑1 backup approver group within 5s, including order ID, requester ID, action scope, and expiry time And the request status changes to "Escalated‑T1" and is visible on the approver queue And if any Tier‑1 approver approves, elevation activates within 3s with the configured scope and TTL And if any Tier‑1 approver rejects, the request is closed and the requester is notified within 3s And duplicate approvals are deduplicated; only the first definitive decision applies
Multi‑Tier Escalation Continuity and Auditable Trace
Given an escalation chain configured with tiers T1..Tn and per‑tier SLAs When a tier’s SLA elapses without a decision Then the next tier is auto‑notified within 5s and the same request ID is reused And escalation stops immediately when any approver approves or rejects And if all tiers are exhausted without decision, the request state becomes "Escalation‑Exhausted" And an immutable audit log entry is recorded for each escalation event capturing request ID, tier, recipients, timestamps, delivery outcomes, and actor decisions
Break‑Glass Path Requires Strong Auth and Justification
Given Escalation‑Exhausted or an authorized user invokes break‑glass for a pending request When break‑glass is initiated Then the system requires strong authentication: WebAuthn (FIDO2) or TOTP for the approver plus re‑authentication of the requester And the approver must select a reason code and enter a free‑text justification of at least 50 characters And upon success, elevation is limited to the specified action(s) only and a TTL not exceeding 10 minutes And the UI clearly labels the session as "Break‑Glass" with remaining time visible to both parties
High‑Severity Alerts on Break‑Glass and Exhausted Escalation
Given a break‑glass approval or an escalation chain is exhausted When the event occurs Then a high‑severity alert is sent to configured channels (e.g., Slack webhook and email) within 10s And the alert payload includes request ID, store ID, requester, approver (if any), action scope, TTL, reason code, justification excerpt, and links to audit trail And alert delivery outcomes are tracked; at least one channel must confirm delivery or the system retries with exponential backoff for up to 5 minutes
Offline Fallback Codes for SMS Delivery Failure
Given an approval magic‑link SMS shows delivery failure or is undelivered for > 90s When the requester selects "Use offline fallback code" Then the system accepts a single‑use fallback code matching format XXXX‑XXXX (base32, 8 chars), pre‑scoped to the specific action and a TTL ≤ 5 minutes And upon successful validation, the code is immediately invalidated and removed from the available pool And after three consecutive invalid attempts the fallback entry is locked for 5 minutes and an alert is logged And all fallback code usage is written to the audit log with code ID (masked), actor IDs, timestamps, and outcome
Immediate Post‑Use Revocation and Mandatory Review
Given an elevation was granted via break‑glass or fallback code When the permitted action completes or TTL expires, whichever comes first Then all elevated permissions are revoked within 3s and any tokens or links issued are invalidated And a mandatory review task is created in the admin console with severity High, due in 24 hours, assigned to the store manager And notifications are sent to the assignee and compliance mailbox; overdue tasks auto‑escalate after 24 hours And the requester cannot initiate a new break‑glass within 10 minutes without manager approval
Elevated State UX Indicators & Countdown
"As a team member, I want clear on-screen indicators of my elevated status and time remaining so that I can act confidently and finish tasks before access expires."
Description

Display clear, persistent indicators when a user is elevated, including allowed actions, remaining time, and request context. Provide pre-expiry warnings, quick request-for-extension, and auto-disable of controls outside the approved scope, ensuring staff understand boundaries and avoid errors.

Acceptance Criteria
Persistent Elevated Banner with Allowed Actions and Countdown
Given I have an active elevation with scope and expiry from the server When I view any page in the operations app Then a non-dismissible elevated-state banner is pinned to the top across all routes and reloads And the banner shows approver name, request context (if provided), list of allowed actions, and a countdown in mm:ss And the countdown updates every 1 second and matches server-derived remaining time within ±2 seconds And when I navigate SPA routes or hard-reload, the banner persists with correct remaining time and scope details And when viewport width < 480px, the banner collapses into a compact bar with an expand control while remaining visible
Pre‑Expiry Warnings and Auto‑Revoke
Given my elevation is active When remaining time reaches 60 seconds Then a visual pre-expiry warning appears and the banner highlights a pre-expiry state once per elevation session And when remaining time reaches 10 seconds, a second warning appears with increased emphasis And when the countdown reaches 00:00, all elevated controls are disabled within 250 ms, the banner changes to "Elevation expired," and the countdown stops And when I attempt any elevated action after expiry, the action is blocked client-side and server-side with a clear message and a "Request extension" call-to-action
Request Extension from Banner or Warning
Given my elevation is active When I tap "Request extension" from the banner or a warning Then a modal opens requiring a reason (minimum 5 characters) and optional notes And when I submit, the banner state changes to "Awaiting approval" showing a spinner and approver target, without changing the current expiry And when the supervisor approves within 2 minutes, the countdown extends immediately to the approved new expiry and the banner returns to normal elevated state without duplicate warnings And when the request is denied or times out, the banner shows "Extension denied/expired" and provides a "Retry request" control
Scope‑Based Enablement and Limits
Given I am elevated with scope "Instant Adjust — Refund up to $50" When I view refund-related UI Then only controls within that scope are enabled and visually marked as elevated And all out-of-scope controls remain disabled with a lock icon and tooltip "Out of scope for current elevation" And when I deep-link to an out-of-scope route, I see an in-app 403 screen with a link back to allowed areas And when I enter an amount exceeding $50, the "Confirm" action is disabled and an inline error explains the remaining allowance And authorization is enforced server-side for all elevated operations
Background, Time Drift, and Offline Resilience
Given my elevation is active When the tab is backgrounded or the device clock changes Then the countdown remains accurate using server time with drift ≤ ±5 seconds And when the client reconnects after being offline, the UI reconciles with the server within 3 seconds and updates the banner and control states accordingly And if the elevation expired while offline, upon reconnect the banner immediately shows "Elevation expired" and elevated actions are blocked And when I return the tab to foreground with less than 60 seconds remaining, the pre-expiry warning is shown if not previously surfaced
Accessibility and Responsive Behavior
Given any elevated state view Then the banner and warnings meet WCAG 2.2 AA color contrast and are reachable via keyboard with visible focus And screen readers announce "Elevation active," approver, allowed actions, and remaining time at most once every 30 seconds until T-10, then assertive at T-10 and expiry And all disabled out-of-scope controls use aria-disabled=true and provide an accessible tooltip message And on mobile (≤ 360px width), the banner occupies ≤ 15% of viewport height, remains visible while scrolling, and all actions including "Request extension" are operable via touch

Break Pause

One tap locks the shared tablet with a privacy shield during breaks or runs, preserving the live board and hiding guest PII. Resume instantly via the active magic link, or auto‑expire after a set window to prevent orphaned sessions and cross‑shift confusion.

Requirements

One-Tap Break Pause Toggle
"As a front-of-house lead, I want to lock the shared tablet in one tap so that guest information stays private during breaks while the live board keeps running."
Description

Implements a prominent, single-action control on the shared tablet that immediately locks the interface and activates a privacy shield while preserving the live curbside board state and connectivity. On activation, all interactive elements are disabled, sensitive data is removed from view, and a minimal pause screen displays elapsed time and instructions to resume. The system remains connected so arrivals continue to be detected and orders updated in the background. Handles intermittent connectivity gracefully, supports kiosk/full-screen mode, and prevents navigation, gesture, or back-button escapes during pause.

Acceptance Criteria
One-Tap Pause Activates Lock
Given the shared tablet displays the live curbside board in an active session When the user taps the Pause control once Then within 500 ms a pause overlay covers the UI and disables all underlying interactions (taps, swipes, keyboard focus) And the overlay shows a privacy shield, an elapsed timer starting at 00:00, and clear instructions to resume
Privacy Shield Redacts PII During Pause
Given the app is in a paused state Then guest names, phone numbers, vehicle identifiers, and order notes are not visible anywhere on screen And in-app notifications and banners do not display PII while paused And text selection, copy, and share actions are disabled on the overlay
Board State Preserved on Resume
Given the board was paused from a specific filtered/sorted view with a particular scroll position and (if applicable) selected order When the session is resumed Then the user returns to the same filters, sort, scroll position, and selection without a full page reload And the elapsed pause timer clears and normal interactions are re-enabled
Background Arrivals and Updates Continue While Paused
Given the device has network connectivity during pause When new arrivals are detected or order statuses change Then the client remains connected and syncs updates in the background And upon resume, the board reflects all updates that occurred during pause within 2 seconds And if connectivity drops during pause, an offline indicator appears on the overlay within 2 seconds and missed updates apply within 5 seconds of reconnect
Resume via Active Magic Link
Given the device holds an active magic-link session and the app is paused When the user taps Resume on the overlay or re-opens via the active magic link in the same browser context Then the app unlocks within 1 second without re-authentication and removes the overlay And if the magic link is expired or opened from a different browser/device, resume is denied and the user is prompted to request a new link
Auto-Expire After Inactivity
Given the app is paused When the elapsed pause time reaches the configured inactivity window (default 15 minutes unless overridden by org settings) Then the session expires and the overlay changes to an Expired state that keeps the board hidden And attempting to resume requires a fresh magic link or sign-in And the previously paused session cannot access the board without re-authentication
Kiosk/Fullscreen and Navigation Lock During Pause
Given the app is paused in kiosk/full-screen mode When the user attempts system back, browser navigation, pull-to-refresh, or swipe gestures Then the pause overlay remains in focus and no underlying data is exposed And the URL/route does not change to reveal content And if kiosk/full-screen is exited by the OS, the app remains on the paused overlay with no PII visible
Magic Link Resume Authentication
"As a staff member, I want to resume the paused board instantly via our magic link so that I can get back to serving guests without re-login delays."
Description

Enables instant session resume via an active, time-bound magic link (SMS/bookmark) without requiring password entry. When invoked from an authorized device, the link securely unlocks the paused board, restoring the exact pre-pause state. Token binding ties the link to the location and device, with replay protection and configurable TTL. Includes safe fallback to manual verification if the link has expired, and revokes the paused state atomically to avoid concurrent resumes.

Acceptance Criteria
Authorized Device Resume Within TTL
Given a board at Location A is in Paused state and a magic link with TTL=30 minutes was issued to Device D bound to Location A When the user taps the magic link on Device D within 30 minutes of issuance Then the board unlocks in 2 seconds or less and the privacy shield is removed And the exact session is restored without requiring a password or additional prompts And the paused state is revoked and the token is marked as used And an audit event "resume_success" is recorded with timestamp, location, device fingerprint, and token ID (redacted)
Unauthorized Device or Location Is Denied
Given a magic link is bound to Device D and Location A When the link is opened on Device E or on any device not matching the binding or at a browser reporting a different Location B Then the board remains locked and no PII is displayed And the system presents an access-denied message with a "Request verification" action And the token remains unconsumed and reusable by the bound device within TTL And an audit event "resume_denied_binding_mismatch" is recorded including attempted device fingerprint and IP And repeated failed attempts (5 within 10 minutes) are rate-limited for 15 minutes
Expired Link Triggers Manual Verification
Given a board is Paused and the issued magic link TTL has elapsed When the user opens the expired link Then the system presents the configured manual verification challenge (e.g., manager PIN or SMS code) And PII remains hidden until the challenge succeeds And on successful verification the board unlocks, the paused state is revoked, and a new magic link is issued And on failure the board remains locked and the user is prompted to retry or contact a manager And audit events "resume_expired" and "manual_verification_{success|failure}" are recorded
Single-Use Token and Replay Protection
Given a magic link token has been used to resume a session successfully When the same link is used again by any device Then the request is rejected with a non-revealing error and no state changes occur And the response indicates the token is no longer valid And an audit event "resume_replay_blocked" is recorded And no duplicate unlocks or notifications are produced
Atomicity Under Concurrent Resume Attempts
Given the board is Paused and two clients open the valid magic link within 100 ms of each other When both resume requests reach the server Then exactly one request transitions the board from Paused to Active And the other request receives an "already_resumed" response with no PII exposure And exactly one "resume_success" audit event is recorded and zero duplicate state transitions occur And the final board state is Active with a single active session
Exact Pre-Pause State Restoration
Given the board was Paused with specific filters, sorting, active orders, bay assignments, and notification preferences And new orders may have arrived during the pause When the magic link successfully resumes the session Then the UI restores the pre-pause filters, sorting, and layout on the same device And all order/bay data reflects the latest server state, including orders arriving during pause And timers and counters resume with accurate elapsed times (no reset or double-advance) And no duplicate sounds/alerts are emitted on resume
Configurable TTL Enforcement and Audit/Redaction
Given the location TTL setting is configurable between 5 and 120 minutes with a default of 30 minutes When an admin sets TTL to 10 minutes and issues a new magic link Then the link expires 10 minutes after issuance and is rejected if opened at 10:01 or later And previously issued links retain their original TTL and are not retroactively changed And audit logs capture the TTL applied at issuance and outcome at use time And all logs/analytics redact the raw token value and referrers do not leak the token
Configurable Auto-Expire Pause Window
"As an owner-operator, I want paused sessions to auto-expire after a set time so that new shifts don’t inherit orphaned sessions or exposed screens."
Description

Adds a location-level setting to define the maximum pause duration (e.g., 5–45 minutes). When the timer elapses, the session auto-expires: the tablet exits pause to a safe screen with no PII and requires a fresh magic link to continue. An optional warning appears before expiry, and owners can enable SMS or in-app nudge to the team. Auto-expire prevents orphaned paused sessions across shifts, clears stale tokens, and reduces cross-shift confusion.

Acceptance Criteria
Auto-Expire After Configured Pause Duration
Given a location has Auto-Expire Pause set to 20 minutes And a shared tablet enters Break Pause at 10:00:00 local time When no resume action occurs before expiry Then the system auto-expires the session between 10:20:00 and 10:20:10 And the tablet exits Pause state automatically And an audit log entry "pause_auto_expired" is recorded with timestamp and elapsed duration
Resume Requires Fresh Magic Link Post-Expiry
Given a Break Pause session has auto-expired When a user opens the previously active magic link Then access is denied with HTTP 401 and message "Link expired" And the live board is not shown When an authorized owner generates a new magic link and the user opens it Then the live board loads successfully and the session state is "Active" And an audit log entry records the new token id and login time
Optional Expiry Warning Banner Timing
Given Auto-Expire Pause is 30 minutes and warning offset is 2 minutes enabled And Break Pause began at 12:00:00 local time When the clock reaches 12:28:00 Then a prominent warning displays within 5 seconds stating "Pause will expire in 2 minutes" And the warning remains visible until either Resume is tapped or auto-expire occurs And if no action is taken, auto-expire triggers at 12:30:00 ±10 seconds
Owner-Configurable Pause Duration Range Enforcement
Given a location owner opens Settings > Break Pause When entering a value below 5 or above 45 minutes Then the Save action is disabled and an inline error reads "Enter 5–45 minutes" When entering an integer between 5 and 45 inclusive Then the Save action enables and the value is persisted for the location And refreshing the settings page shows the saved value And the next Break Pause uses the saved duration
SMS/In-App Nudge Before Expiry (If Enabled)
Given nudge notifications are enabled and a team recipient list is configured And Auto-Expire Pause is 25 minutes with a nudge offset of 3 minutes And Break Pause started at 14:00:00 local time When the clock reaches 14:22:00 Then an in-app banner is delivered to online staff and an SMS is sent to configured numbers within 30 seconds And the message includes the location name and "3 minutes until auto-expire" And no nudge is sent if notifications are disabled or the recipient list is empty
Safe Screen With No PII On Auto-Expire
Given the live board contained guest names, phone numbers, order identifiers, vehicle details, and bay assignments before Pause When auto-expire triggers Then the tablet displays a safe screen with no guest names, phone numbers, order identifiers, vehicle info, or bay assignments visible And navigating back to the live board requires a fresh magic link And OS/app previews are masked so no PII is visible in the task switcher
Stale Token Revocation On Auto-Expire
Given multiple magic link tokens exist for the paused session/device When auto-expire occurs Then all tokens associated with that paused session are invalidated immediately And any request using those tokens returns HTTP 401 "expired" And the audit log includes the number of tokens revoked and their ids (redacted as appropriate)
Privacy Shield Redaction Layer
"As a manager, I want a privacy shield that hides guest PII while the tablet is paused so that we stay compliant and discreet at the counter."
Description

Renders a privacy-first overlay during pause that fully masks guest PII (names, phone numbers, license plates, notes) while optionally showing non-sensitive operational indicators such as order count and bay occupancy. The shield blocks touch input, dims the display, and supports accessibility (screen reader-friendly labels hidden from content) and different lighting conditions. Prevents data exposure via in-app screenshots by redacting underlying DOM elements, and ensures no content is revealed through scrolling, focus changes, or system dialogs.

Acceptance Criteria
Activate Privacy Shield on Pause Tap
Given a staff user is viewing the live board on the shared tablet When the user taps Break Pause Then a full-screen privacy shield renders within 300 ms And all underlying PII-bearing DOM nodes are programmatically redacted (text cleared or replaced, aria-hidden="true", data-pii masked) And the display is dimmed via an overlay with opacity between 0.5 and 0.7 And only the shield header and permitted non-sensitive indicators are visible And the shield state persists across route changes and page refresh while pause is active
Comprehensive PII Redaction
Given pause is active and orders contain guest name, phone, license plate, and notes in the UI and accessibility tree When inspecting rendered pixels, DOM innerText, aria-labels/alt text/value attributes, clipboard selections, and in-page search Then no PII strings are visible, selectable, searchable, or exposed to assistive technologies And all elements marked data-pii="true" display standardized placeholders (e.g., "████") with no partial masking And PII remains redacted for any background updates received while paused
Non‑Sensitive Indicators Only
Given pause is active When order count or bay occupancy changes Then the visible indicators update within 5 seconds And the indicators contain no embedded PII (validated by regex checks for phone, email, name, and plate patterns) And no drilldowns, tooltips, or gestures are enabled on these indicators while paused
Input, Scroll, and Focus Lockdown
Given pause is active When a user attempts to tap, scroll, pinch-zoom, use keyboard navigation (Tab/Shift+Tab/Arrows), or open context menus Then no underlying interactive element receives pointer or keyboard events And focus is trapped within the shield container with a deterministic tab order; Escape does not dismiss the shield And page scroll position is locked and remains unchanged And selecting/copying text or using browser Find-in-page does not surface underlying content
Accessibility and Screen Reader Compliance
Given pause is active and a screen reader is running When the shield appears Then the shield has role="dialog" and aria-modal="true" with the accessible name "Privacy shield active" And underlying content containers are set aria-hidden="true" And shield text and controls meet WCAG 2.1 AA contrast (>= 4.5:1) And screen reader navigation exposes only generic, non-PII labels for permitted indicators And prefers-reduced-motion is honored (no animation beyond a single fade <= 200 ms)
In‑App Screenshot and Print Redaction
Given pause is active When the browser print dialog is opened or a DOM capture (e.g., canvas-based screenshot) is initiated from within the app Then the captured output contains only the shield UI and permitted indicators And no underlying PII text or images are present in the output pixels or PDF text layer And window.getSelection() returns empty and copy events contain no PII while paused
Lighting and Visibility Compliance
Given device light and dark modes and varying device brightness settings When the shield is active Then shield text and icons maintain contrast >= 4.5:1 against the background in both modes And the dimming overlay reduces perceived luminance by at least 40% and at most 80% And at 200% browser/page zoom the shield layout remains readable without clipping or overlap
Role-Based Access and Audit Trail
"As an operator, I want pause and resume actions to be permissioned and fully logged so that I can control access and review activity when needed."
Description

Introduces permissions controlling who can pause or resume, with fine-grained roles for staff, leads, and managers. Every pause/resume event is logged with timestamp, user identity (or device fingerprint if anonymous), location, device ID, duration, trigger method (button, auto-expire, magic link), and optional reason code. Logs are exportable and filterable in admin for compliance, training, and incident review.

Acceptance Criteria
Role Permissions: Staff, Lead, Manager Controls for Pause/Resume
Given a logged-in Staff user on a device assigned to Location A, When they tap Pause, Then the action is permitted and the session state becomes Paused. Given a Staff user at Location A viewing a paused session from Location B, When they attempt Resume, Then the action is denied with an "Insufficient permissions" message and no state change occurs. Given a Lead assigned to Location A, When they Pause or Resume any device at Location A, Then the action succeeds. Given a Manager with org-wide scope, When they Pause or Resume any device across locations, Then the action succeeds.
Magic Link Resume and Auto‑Expire Enforcement
Given a paused session with a 15-minute TTL and an active magic link, When an authenticated Lead opens the link within 15 minutes, Then the session resumes and trigger_method is recorded as "magic_link" with the Lead's user ID as actor. Given the same magic link opened after 15 minutes, When any user opens it, Then the link is invalid and no resume occurs, and an audit entry records trigger_method "auto_expire" for the session expiration. Given an anonymous user opens a valid magic link within TTL, When policy allows link-based resume without login, Then the session resumes and the audit entry records actor_user_id null and a device_fingerprint value.
Comprehensive Audit Fields on Pause/Resume Events
Given any Pause or Resume event (manual button, magic link, or auto-expire), When the event is logged, Then the log entry includes: timestamp (UTC ISO-8601), location_id, device_id, event_type (pause|resume|auto_expire), trigger_method (button|magic_link|auto_expire), actor_user_id if authenticated else device_fingerprint, optional reason_code, and duration_seconds where applicable. Given a Resume following a Pause, When duration is calculated, Then duration_seconds equals resume_timestamp minus pause_timestamp for that session. Given an optional reason_code is entered on Pause, When the event is logged, Then reason_code is stored and displayed in admin views and exports.
Reason Code Requirement Configuration
Given org policy requires a reason_code on Pause, When a user attempts to Pause, Then the system prompts for a reason_code and blocks Pause until provided. Given org policy sets reason_code optional, When a user Pauses without entering a reason, Then the Pause proceeds and the log stores reason_code as null. Given org policy disallows free text and limits to predefined reason codes, When a user provides a reason, Then the input must match an allowed value or the action is blocked.
Admin Audit Log Filtering and Review
Given an admin user with access to Audit Logs, When they filter by date range, location, device, user, role, trigger_method, reason_code, and duration range, Then the results update to include only matching records. Given a large dataset (<=50,000 events), When filters are applied, Then the filtered results return within 2 seconds and are paginated consistently. Given a selected record, When viewed in detail, Then all stored fields are visible and read-only.
Audit Log Export (CSV) with Permissions
Given an admin user with export permission, When they export the current filtered results, Then a CSV downloads containing headers and all matching rows with all defined fields in UTC and semicolon-safe CSV formatting. Given the current filter yields N results, When export completes, Then the CSV row count (excluding header) equals N. Given a user without export permission, When they attempt export, Then the action is denied with an appropriate error and no file is generated.
Background Arrival Continuity and Catch-up Alerts
"As a curbside runner, I want arrivals to keep processing during pause and a quick catch-up view on resume so that I don’t miss guests and can act immediately."
Description

Ensures backend arrival detection, bay assignment, and order status updates continue uninterrupted while the tablet is paused. On resume, the UI presents a concise catch-up banner summarizing arrivals and changes that occurred during pause, with quick actions to acknowledge or reassign bays. Optional gentle audio cue or SMS can alert staff to new arrivals during extended pauses without exposing PII on the screen.

Acceptance Criteria
Pause Mode Maintains Backend Arrivals and Bay Assignments
Given the tablet is in Break Pause with an active magic link When a guest triggers the 'I'm here' link during the pause Then the backend records the arrival within 2 seconds And automatically assigns an available bay per allocation rules And updates the order status to 'Arrived' with the assigned bay And if no bay is available, the order is placed in 'Awaiting Bay' queue And these updates are persisted server-side and retrievable on resume And no guest PII is rendered on the paused screen
Catch-up Banner on Resume Summarizes Changes During Pause
Given one or more arrivals or bay/status changes occurred during the pause When staff resumes from Break Pause via the active magic link or unlock action Then a catch-up banner appears within 1 second And summarizes counts (new arrivals, reassignments, pickups/cancellations) and lists up to the 10 most recent events And each item shows bay number, event type, and time elapsed without exposing PII And the banner persists until dismissed or all items are acknowledged And a 'View all' action reveals the full list if more than 10 events occurred
Quick Acknowledge and Reassign Actions from Catch-up
Given the catch-up banner is visible with event items When staff taps 'Acknowledge' on an arrival item Then the order status updates to 'Acknowledged' with a server timestamp and the item is removed from the list When staff taps 'Reassign' on an arrival item Then a bay picker shows only currently available bays and blocks conflicts And upon confirmation, the backend updates the bay assignment, writes an audit log entry, and the UI reflects the change within 1 second And if the selected bay becomes unavailable before confirmation, the UI shows a non-blocking error and prompts for another selection
Optional Audio Cue During Extended Pause Without PII
Given 'Audio cue during pause' is enabled and the tablet has been paused longer than the configured threshold (default 2 minutes) When a new arrival is detected during the pause Then a gentle audio chime plays within 2 seconds And no spoken or displayed PII accompanies the cue And multiple arrivals within a 60-second cooldown window trigger only one chime grouped notification And audio respects the tablet's system mute/volume settings
Optional SMS Alert to On-Shift Number During Extended Pause
Given 'SMS alert during pause' is enabled and one or more on-shift phone numbers are configured And the tablet has been paused longer than the configured threshold (default 2 minutes) When a new arrival is detected during the pause Then an SMS is sent within 10 seconds containing location name, count of new arrivals, bay number if assigned (or 'Awaiting Bay' if not), and a one-time resume magic link And the SMS contains no guest names, phone numbers, or order item details And duplicate SMS alerts are suppressed within a 60-second cooldown window And delivery errors and bounces are logged for review
Auto-Expiration of Paused Session and Orphan Prevention
Given an auto-expire window is configured (default 30 minutes) When the pause exceeds the auto-expire window without a resume Then the paused session auto-expires and the lock screen returns to a neutral privacy shield with no contextual hints And any magic link older than the auto-expire window is invalidated And on next authenticated login, a catch-up summary still reflects all events that occurred during the pause based on server state And cross-shift users cannot access the prior session without re-authentication
Event Ordering, Conflict Handling, and Data Integrity During Pause
Given multiple arrivals and bay changes occur while the tablet is paused When the tablet resumes Then events are displayed in chronological order by server timestamp And no bay is double-assigned; conflicts are resolved server-side and the catch-up shows the final valid assignment (e.g., 'Reassigned to Bay 4') And the total number of events recorded during the pause equals the number displayed plus any auto-resolved items shown in a grouped summary count And all events and user actions are audit logged with actor, action, and timestamp And replaying the resume or refresh is idempotent and does not duplicate events

Access Profiles

Guests can opt in to save accessibility preferences (wheelchair space, trunk‑first, passenger‑side handoff, wider bay, low‑voice/no‑knock). On the next check‑in from the same number, CurbPing auto‑applies those needs—pre‑filling instructions, prioritizing the nearest suitable bay, and discreetly briefing staff. Result: no repeated explanations, faster setup, and a more dignified experience.

Requirements

Consent-Based Access Profile Opt-in (SMS/Web)
"As a guest with accessibility needs, I want to save my curbside preferences once so that I don’t have to repeat them at every pickup."
Description

Provide an opt-in flow presented post-check-in or via persistent SMS link that invites guests to save accessibility preferences. The web form captures predefined options (wheelchair space, trunk-first, passenger-side handoff, wider bay, low-voice/no-knock) and optional notes, with clear consent language and data-use purpose. No app install; supports screen readers and WCAG AA. On submit, store profile keyed to phone number and location scope by default, with option to make it cross-location within the brand. Send confirmation SMS and link to manage preferences. Ensure fail-safe skip path that never blocks check-in.

Acceptance Criteria
Post-Check-In Opt-In Prompt with Non-Blocking Skip
Given a guest completes curbside check-in via the web link When the confirmation screen renders Then display an invitation to save accessibility preferences with primary “Save preferences” and secondary “Skip for now” actions And the prompt loads within 3 seconds on a 3G network And selecting “Skip for now” immediately returns the guest to the standard post-check-in state without additional steps And closing/ignoring the prompt does not block or delay the check-in completion And no preferences are saved unless the guest explicitly submits the form
Persistent SMS Link to Manage Accessibility Preferences
Given the guest’s phone number is SMS-capable and has not opted out When the system sends the standard check-in confirmation SMS Then include a single persistent “Manage accessibility preferences” link in the message And tapping the link opens the manage page in the browser within 2 seconds on LTE And the link is unique per guest-brand and remains valid for at least 30 days And the manage page loads pre-filled with any existing saved preferences for that number
Accessibility Preferences Form Capture and Validation
Given the guest opens the accessibility preferences form When the page loads Then present predefined options for wheelchair space, trunk-first, passenger-side handoff, wider bay, and low-voice/no-knock as selectable controls And include an optional notes field accepting up to 200 characters, rejecting HTML/script input, and trimming leading/trailing whitespace And the form prevents submission if no change has been made and no consent given And all fields are operable by keyboard only, have accessible labels, visible focus, and pass WCAG 2.1 AA contrast And form validation errors are announced to screen readers and displayed inline next to the offending field
Explicit Consent and Data-Use Disclosure with Versioned Audit
Given the guest is viewing the preferences form When consent language is displayed Then the consent checkbox is unchecked by default and required to enable the Save action And the consent copy clearly states data-use purpose and storage scope in plain language And upon submit, store an audit record containing phone number (normalized E.164), brand ID, location ID, scope selection, timestamp (UTC), source (web/SMS), and consent version And upon successful save, send a confirmation SMS within 60 seconds that includes the brand name, a manage link, and STOP instructions
Profile Storage and Scope (Location-Default, Cross-Brand Option)
Given the guest submits the form with valid input and consent When saving the profile Then store the profile keyed by normalized phone number + brand ID And set scope to current location by default And allow the guest to opt into cross-location sharing within the same brand before saving And when cross-location is selected, subsequent check-ins at any location under that brand can access the profile And subsequent saves perform an upsert that replaces prior values and updates the audit trail
Auto-Apply Preferences on Subsequent Check-Ins
Given a saved profile exists for the guest’s phone number within the applicable scope When the guest checks in again at a covered location Then pre-fill on-screen instructions with the saved accessibility preferences And the bay assignment algorithm restricts to suitable bays (e.g., wider/accessible) and selects the nearest available; if none are available, choose the next best and flag assistance needed And staff are discreetly briefed via internal dashboard badges/notes visible only to staff assigned to the order And the guest can override preferences for this order without changing the saved profile unless they explicitly resave
Fail-Safe Behavior and Error Handling
Given any error occurs during loading, submission, storage, or SMS sending for the preferences flow When the guest completes or skips the flow Then the curbside check-in remains completed and unblocked And if profile storage fails, show a non-intrusive error message and do not save partial data And if the confirmation SMS fails to send, show an on-screen confirmation with the manage link and log the failure for retry And skipping or closing the flow adds no more than 1 second to the check-in path
Phone Number Profile Matching and Auto-Apply
"As a returning guest, I want my saved accessibility needs to apply automatically so that my pickup is set up correctly without extra steps."
Description

On each new check-in, match incoming phone number to an existing access profile, respecting tenant boundaries and opt-in scope. When found, auto-apply preferences to the order: pre-fill special instructions, tag the session with accessibility flags, and attach profile metadata to dispatch events. Handle edge cases: multiple profiles (choose most recent), number changes (offer one-time link to claim), and opt-out. Log whether preferences were applied for analytics.

Acceptance Criteria
Auto-apply preferences on matching, opted-in profile within tenant
Given a new curbside check-in originates from phone number N that has a single, opted-in accessibility profile under the current tenant When the guest opens the 'I'm here' link and completes check-in Then the system pre-fills the special instructions field with the profile's instructions before the guest submits And the active session is tagged with the profile's accessibility flags And the dispatch event payload for this session includes the profile metadata required for staff briefing (flags, instruction text, profileId) And the application of preferences is completed before the order is dispatched to staff
Prevent cross-tenant profile application
Given the same phone number N has profiles in other tenants but none in the current tenant When a check-in occurs under the current tenant Then no preferences are applied And no profile metadata from other tenants is exposed And the analytics log records applied=false with reason=NoTenantMatch
Resolve multiple profiles by most recent update
Given phone number N has multiple profiles within the current tenant When a check-in occurs Then the profile with the latest updatedAt timestamp is selected And if updatedAt values are equal, the profile with the latest createdAt timestamp is selected And the selected profile's ID is included in both the dispatch payload and analytics logs And that profile's preferences are applied to the session
Honor opt-in scope and opt-out
Given a profile exists for phone number N under the current tenant with optIn=false or status=optedOut When a check-in occurs Then no preferences are applied And the dispatch payload contains no accessibility metadata from that profile And analytics logs applied=false with reason in {OptedOut, NotOptedIn}
Offer and process one-time claim link for number change
Given no matching profile is found for the incoming phone number N When the check-in completes Then the guest receives an SMS-safe, single-use claim link to associate an existing accessibility profile to N And the claim link expires after its configured TTL and cannot be reused once redeemed And upon successful claim, the profile is linked to N and will auto-apply on the next check-in And analytics logs claimOffered=true and claimCompleted=true/false
Analytics logging of preference application outcome
Given any check-in event When profile matching is attempted Then a log entry is created containing tenantId, applied (true/false), reasonCode, matchedProfileId (nullable), and timestamp And the log entry is retrievable via the analytics reporting interface for the tenant
Dispatch events carry discrete accessibility metadata only
Given preferences are applied When dispatch events are emitted to staff systems Then the payload includes only the applied accessibility flags, special instruction text, profileId, and applied=true indicator related to accessibility And it excludes any additional profile fields not required for fulfillment And staff UIs display discrete indicators corresponding to the flags without exposing unnecessary details
Discreet Staff Briefing and Instruction Pre-fill
"As a curbside runner, I want clear, discreet cues about a guest’s needs so that I can fulfill the handoff correctly and respectfully."
Description

Present concise, discreet indicators of a guest’s accessibility needs in the staff dashboard and picker UI without exposing sensitive details to bystanders. Auto-populate the order’s special instructions, display standardized icons and short phrases (e.g., “Trunk-first,” “Passenger-side”), and surface a one-tap quick brief for attendants. Ensure the signals propagate to any printed tickets and two-way SMS templates. Provide audible and visual cues configurable per location while defaulting to low-disclosure wording.

Acceptance Criteria
Auto-population of Special Instructions from Saved Preferences
Given a returning guest with saved accessibility preferences and an inbound "I'm here" check-in from the same phone number When the order is surfaced in the staff dashboard or picker UI Then the special instructions field auto-populates with standardized short phrases for each applicable preference And the pre-filled text is editable by staff prior to acknowledgment And no text beyond the standardized phrases is inserted And if the guest has no saved preferences or has opted out, the special instructions field remains unchanged And the pre-fill occurs within 500 ms of check-in being associated to the order And an audit note records "Instructions auto-filled from Access Profile"
Discreet Indicators in Staff Dashboard and Picker UI
Given an active order with saved accessibility preferences When the order card/detail view is opened in the staff dashboard and picker UI Then only standardized icons and short phrases (<= 20 characters each) are displayed for each preference And no medical terms or diagnosis-related text is shown And the indicators are visible within 2 seconds of order activation And a tooltip or aria-label mirrors the same short phrase without additional detail And indicators are hidden when the display is set to Public Screen mode
One-Tap Quick Brief for Attendants
Given an active order with at least one accessibility preference When an attendant taps the Quick Brief action on the order Then a brief sheet/modal appears summarizing the preferences using icons and short phrases only And the brief opens within 300 ms on target devices And it contains a single primary action (Got it) and dismisses with one tap or ESC And no PII beyond first name and bay number is shown And an analytics event "quick_brief_viewed" is logged with order ID and timestamp
Propagation to Printed Tickets
Given printing is enabled for the location When a ticket is printed for an order with accessibility preferences Then the ticket includes the standardized short phrases in brackets (e.g., [Trunk-first], [Passenger-side]) And no icons or wording that reveal medical conditions are printed And the accessibility line prints in a dedicated row, legible at 10pt+ on 80mm and 58mm paper And the same content appears on reprints And if no preferences exist, no accessibility line is printed
Propagation to Two-Way SMS Templates
Given staff reply using a predefined SMS template to an order with accessibility preferences When the template renders dynamic variables Then the message includes only low-disclosure wording for needs (e.g., "We’ll bring to passenger side") when applicable And prohibited terms list is not present in the rendered SMS And if the guest opted out of sharing preferences in messages, no needs-related text is inserted And staff can insert or remove the needs tag with a single tap
Location-Configurable Audible and Visual Cues
Given location settings allow configuring audible and visual cues for accessibility needs When an order with preferences is activated or updated Then a visual cue appears on the order card using low-disclosure text and has a contrast ratio >= 4.5:1 And an audible chime plays at the configured volume unless Quiet Mode is enabled And both cues can be toggled per location and default to enabled visual + low-volume audible And no spoken TTS of the preferences is used by default
Privacy and Low-Disclosure Safeguards
Given any staff-facing view, printout, notification, or SMS generated for an order with preferences When the content is rendered Then only the approved standardized phrases list is used to describe needs And no freeform notes from the Access Profile are auto-exposed And users without the "See Accessibility Indicators" permission do not see the indicators And an automated test verifies 0 occurrences of prohibited terms across UI, print, and SMS outputs
Accessibility-Aware Bay Prioritization
"As an operations manager, I want the system to route guests to suitable bays automatically so that arrivals are faster and access needs are met."
Description

Extend the bay assignment engine to evaluate bay attributes (width, wheelchair space, proximity to entrance) and prioritize the nearest suitable bay that satisfies the guest’s profile. Support configurable bay attribute catalog per location and real-time availability. When no ideal bay exists, assign the best alternative and flag for manual override. Expose assignment rationale in the staff UI and log decisions for tuning.

Acceptance Criteria
Nearest Suitable Bay for Wheelchair Profile
Given a guest with a saved accessibility profile requires_wheelchair_space=true And bays A, B, C exist with attributes where A.wheelchair_space=true at 20m, B.wheelchair_space=true at 35m, C.wheelchair_space=false at 10m And bays A, B, C are available When the guest checks in from the same phone number Then the system assigns bay A And the assignment reason includes "nearest suitable bay with wheelchair space" and chosen_distance=20m
Configurable Bay Attribute Catalog per Location
Given location L defines an attribute catalog: width={standard,wide}, wheelchair_space=boolean, handoff_side={driver,passenger}, trunk_access=boolean, proximity_to_entrance=meters And bays at L have attributes validated against the catalog When an admin adds low_voice_zone=boolean to the catalog and sets bay 5.low_voice_zone=true Then the assignment engine evaluates low_voice_zone in suitability immediately without service restart And attempts to save unknown attributes are rejected with error "unknown_attribute" and no partial updates occur
Real-Time Availability and Collision Avoidance
Given two guests G1 and G2 initiate check-in within 2 seconds And only one available bay (Bay 3) satisfies both of their required attributes When the assignment engine processes both check-ins Then exactly one guest is assigned Bay 3 And the other guest is assigned the next best available bay per prioritization rules And no bay is double-assigned during the overlapping window
Best Alternative with Manual Override Flag
Given a guest requires wheelchair_space=true and width=wide And no available bay satisfies both attributes but Bay 7 satisfies wheelchair_space=true only When the assignment is computed Then Bay 7 is assigned as the best alternative And the assignment is flagged "Needs manual override" And the staff UI presents a one-click Reassign control for this order
Assignment Rationale Shown in Staff UI
Given an automatic bay assignment is completed When staff opens the order card Then the card displays matched_profile_attributes, chosen_bay_id, suitability_score, distance_to_guest, evaluated_bays_count, top_rejected_bay_id with rejection_reason, and fallback_used=true|false
Decision Logging for Tuning and Audit
Given any bay assignment event (automatic or manual) When the event is logged Then the record includes timestamp, location_id, anonymized_guest_id, profile_attributes, candidate_bays with attributes, availability_snapshot, suitability_scores, chosen_bay_id, fallback_flag, override_user_id (if any) And logs are queryable by location_id and date range
Passenger-Side Handoff Orientation Constraint
Given a guest profile requires handoff_side=passenger And bays 2 and 4 support passenger-side approach while bay 1 does not When the guest checks in Then only bays 2 and 4 are considered suitable And if neither is available, the selected fallback bay is flagged and staff instructions include "passenger-side not available"
Profile Privacy, Management, and Data Retention
"As a privacy-conscious guest, I want control over my saved accessibility data so that I can manage what’s kept and revoke it when I choose."
Description

Implement secure storage and handling of access profiles with encryption at rest and in transit, explicit consent records, and least-privilege access. Provide guests with SMS-accessible links to view, edit, or delete their preferences and opt-out at any time. Enforce retention limits and automatic purge for inactive profiles after a configurable period. Maintain audit logs of profile creation, updates, and application events for compliance and support.

Acceptance Criteria
SMS Consent Capture and Record
Given a guest opens the SMS link to save accessibility preferences When the page loads Then clear consent language describing storage, use, retention period, and opt-out is displayed prior to any save action Given the consent language is displayed When the guest selects "I agree" and saves preferences Then a consent record is stored with phone_number, venue_id, timestamp (UTC), consent_version, IP (hashed), user_agent, and a checksum of the consent text Given a guest declines consent When the guest attempts to save Then no profile is created or updated and a message confirms that preferences will not be saved Given an existing consent record When the consent text version changes Then the next edit requires re-consent and creates a new consent record
Encrypted Storage and Transport
Given client-server communication for profile management When requests are made Then TLS 1.2+ with HSTS and modern ciphers is enforced and HTTP is redirected to HTTPS Given profile data is stored at rest When saved in databases, search indexes, or backups Then data is encrypted with AES-256 using KMS-managed keys rotated at least every 90 days Given a key rotation event occurs When services continue operating Then decryption/encryption proceeds without data loss and access with retired keys is denied and logged Given external SSL testing When scanned by SSL Labs Then the public endpoint receives an A grade or higher
SMS Self-Service Profile Management Links
Given a guest requests to manage preferences via SMS (HELP or manage link after check-in) When the system generates a management link Then the link is single-use, bound to the phone number, and expires in 15 minutes with a maximum of 5 attempts before lockout Given the guest opens a valid link before expiry When the profile page loads Then current preferences are displayed and editable, and the phone number is masked to last 4 digits Given the guest saves changes When submission succeeds Then updates are persisted immediately and a confirmation SMS is sent within 10 seconds Given the guest chooses Delete Profile When deletion is confirmed Then the profile is removed from the primary datastore within 24 hours, auto-apply is disabled, and a deletion confirmation SMS is sent; subsequent check-ins do not auto-apply preferences Given an expired or invalid link When accessed Then access is denied and the guest is offered a new link via SMS
Least-Privilege Staff Access Controls
Given role-based access is configured When a Runner is logged in Then they can only view applied instructions for their assigned active orders and cannot view or edit saved profiles Given a Host is logged in When an active order is selected Then they can view applied instructions for that order but cannot bulk browse or export profiles Given a Manager is logged in When viewing a profile with consent on record Then they can view/edit preferences and view consent records; edits are attributed to their user ID in the audit log Given any role attempts to access a profile outside their scope When the request is made Then the system returns HTTP 403 and writes an audit entry with actor role, timestamp, and reason Given any authenticated staff user is idle When 15 minutes elapse without activity Then their session is revoked and re-authentication is required
Inactivity-Based Retention and Auto-Purge
Given a venue retention policy is configured (default 180 days, configurable range 30–365 days) When a profile has no activity for the configured period Then the profile is automatically purged during the next nightly job Given a profile is purged When purge completes Then references in caches/search are removed within 1 hour and analytics references within 24 hours Given backups include the purged profile When backup retention elapses Then the purged data becomes irrecoverable within 30 days of purge Given the nightly purge job runs When it completes Then a report is generated with counts purged by venue and any failures, and retries are scheduled for failures within 24 hours
Comprehensive Audit Logging and Redaction
Given profile lifecycle events occur (create, update, apply, access, delete, consent, opt-out) When any such event happens Then an immutable audit record is written with fields: profile_id, venue_id, actor_id/role or system, phone_hash (SHA-256 with salt), event_type, UTC timestamp, request_id, and a delta summary; no full phone number or full preference text is stored Given an authorized Manager or Support user requests an audit export When filters (date range, event types) are applied Then a CSV is generated within 60 seconds and available for 24 hours via a signed URL Given an audit record is viewed in the UI When displayed Then sensitive fields are redacted and the record is read-only Given a direct modification of audit storage is attempted When a write/update/delete bypasses the append-only API Then the integrity monitor raises an alert and blocks the operation
Operator Configuration and Reporting
"As a restaurant owner, I want to configure accessibility options and see their impact so that I can tailor curbside service and improve outcomes."
Description

Add admin settings for operators to enable Access Profiles, customize the list of supported accessibility options and bay attributes, define default behaviors (e.g., auto-apply scope, briefing verbosity), and manage location-specific overrides. Provide reporting on adoption rates, application rates, and impact on wait times and reassignment incidents, exportable as CSV. Include role-based permissions for configuration access.

Acceptance Criteria
Enable Access Profiles Feature Toggle
Given an operator with configuration access is on Admin > Accessibility When they toggle Access Profiles to On and click Save Then the setting persists at the organization level and shows as Enabled after page reload And new guest check-ins from numbers with saved profiles auto-prefill applicable fields And the staff view displays a discreet Accessibility indicator when a profile is applied
Customize Accessibility Options Catalog
Given Access Profiles are enabled and the operator has configuration access When the operator adds a new accessibility option with label and internal key and clicks Save Then the option appears in the guest check-in form and staff briefing within 30 seconds When the operator edits the label of an existing option and saves Then the updated label is reflected in new check-ins and staff briefings When the operator disables an option and saves Then the disabled option no longer appears for new check-ins or staff briefings
Define and Use Bay Attributes for Allocation
Given the operator has defined bay attributes (e.g., wider bay, wheelchair-accessible) and mapped them to specific bays When a guest check-in includes preferences requiring specific attributes Then the bay assignment filters to bays that match all required attributes and selects the nearest available bay And if no bay matches, the staff UI prompts for manual assignment and flags the attribute mismatch
Configure Auto-Apply Scope and Staff Briefing Verbosity
Given the operator sets Auto-Apply Scope to "Same phone number within 12 months" and Briefing Verbosity to "Discreet" When a returning guest with a saved profile checks in Then the system auto-applies the saved preferences without re-prompting the guest And the staff briefing renders as a single-line discreet summary (icons or short tags) When the operator changes Briefing Verbosity to "Detailed" and saves Then subsequent check-ins render a multi-line briefing with full text details
Apply Location-Specific Overrides with Inheritance
Given organization-level defaults are configured And a specific location defines overrides for supported options and auto-apply scope When a check-in occurs at that location Then the effective configuration uses the location overrides instead of org defaults And the Admin UI marks overridden fields with an Overridden badge When the location removes an override and saves Then the effective configuration immediately reverts to the organization default
Report Adoption, Application, Wait-Time Impact, and Reassignments
Given historical orders and check-ins exist for a selected date range and location(s) When the operator opens Reports > Accessibility and applies a date range filter Then the dashboard shows: Adoption Rate (% of unique guests who opted in), Application Rate (% of check-ins with profile applied), Median Wait Time with profile vs without (and delta), and Reassignment incidents per 100 orders And all metrics can be filtered by location and date range When the operator clicks Export CSV Then a CSV downloads containing per-day rows with columns: date, location_id, adoption_rate, application_rate, median_wait_with, median_wait_without, delta_wait, reassignments_with, reassignments_without
Enforce Role-Based Permissions for Configuration Access
Given a user with role Owner or Admin When they navigate to Admin > Accessibility Then configuration pages load and settings are editable Given a user with role Staff When they navigate to Admin > Accessibility Then access is denied (HTTP 403 or equivalent) and no configuration data is exposed via UI or API

RampRoute Directions

Sends step‑by‑step approach guidance that highlights curb cuts, ramp paths, covered zones, and accessible entrances—right inside the SMS link. If a bay changes, guests get an instant, accessible reroute. Reduces stress and misparks while ensuring the closest, obstacle‑free path to the door.

Requirements

In‑SMS Accessible Turn‑by‑Turn Directions
"As a guest arriving for curbside pickup, I want step‑by‑step on‑screen directions that highlight ramps, curb cuts, covered paths, and the accessible entrance so that I can reach the correct bay and door without obstacles or confusion."
Description

Deliver step‑by‑step approach guidance directly inside the SMS deep link, rendering a lightweight web view with map overlays for curb cuts, ramp paths, covered walkways, and accessible entrances. Present an easy-to-scan list of steps synchronized with the map, with tappable steps, distance/time indicators, and clear iconography. Implement full WCAG 2.2 AA support (screen reader labels, high contrast, large touch targets) and minimal bundle size for fast load on cellular networks. Integrate with CurbPing’s existing arrival flow and bay assignment to default the destination and seamlessly hand off to the pickup confirmation state. Outcome: guests follow the closest, obstacle‑free path to the correct bay and door, reducing misparks and stress.

Acceptance Criteria
Preselected Destination and Seamless Handoff to Pickup Confirmation
- Given an order has an assigned bay and accessible entrance, When the guest opens the SMS deep link, Then the web view loads with the destination preselected to the assigned bay and the nearest accessible entrance highlighted. - Given the guest approaches within 25 meters of the assigned bay (or taps "I'm at the bay"), When proximity is detected, Then the UI transitions to the pickup confirmation state without a page reload. - Given the guest confirms arrival, When confirmation is sent, Then the staff dashboard receives an "Arrived at Bay <X>" event within 1 second with timestamp and location snapshot, and the guest view shows confirmation status. - Given no bay is assigned, When the guest opens the link, Then the destination defaults to the venue’s primary accessible entrance and prompts to select an available bay.
Fast, Lightweight Load on Slow 4G
- Given a device on Slow 4G (400ms RTT, 1.6 Mbps), When the guest opens the deep link, Then First Contentful Paint ≤ 1.5s and Time to Interactive ≤ 2.5s. - Given the initial page request, When assets are downloaded, Then total gzipped JS+CSS ≤ 120 KB and initial HTML ≤ 20 KB; map tiles and images are lazy-loaded. - Given server processing, When the request hits the API, Then TTFB ≤ 500ms for 95th percentile. - Given a cold cache, When the page is loaded, Then a skeleton UI renders within 800ms and is replaced by content as data arrives.
WCAG 2.2 AA Accessibility Compliance
- Given screen reader usage, When focus enters the view, Then the page title and role=main are announced, all actionable icons have aria-labels, and focus order matches visual order without traps. - Given color and contrast requirements, When UI is rendered, Then text and essential icons meet ≥ 4.5:1 contrast; non-text UI elements meet ≥ 3:1. - Given touch interaction, When buttons/steps are displayed, Then touch targets are ≥ 44x44px with 8px spacing and have visible focus indicators. - Given content reflow, When viewport width is 320px or text is zoomed to 200%, Then content reflows without horizontal scrolling and functionality remains intact. - Given dynamic updates (e.g., reroute), When the route changes, Then changes are announced via aria-live="polite" within 1s and do not steal focus. - Given keyboard-only navigation, When navigating the step list and map controls, Then all functions are operable via keyboard and have a visible focus state.
Synchronized Map and Step List Interaction
- Given a multi-step route, When a step in the list is tapped, Then the corresponding segment is highlighted on the map, centered, and the step card is marked as active. - Given map interaction, When a user taps a numbered segment or maneuver pin, Then the corresponding step in the list scrolls into view and is marked active. - Given progression, When the user advances to the next step, Then remaining distance and ETA update within 1s (using device location if granted; otherwise route-based estimates). - Given accessibility, When a step becomes active, Then a concise announcement like "Step 3: Turn right to ramp in 50 ft" is provided to screen readers.
Instant Accessible Reroute on Bay Change
- Given staff reassigns a guest to a different bay, When the assignment changes, Then the guest’s route updates in the web view within 3 seconds without a full reload. - Given a reroute event, When the route changes, Then the new path preserves accessibility constraints (ramps, curb cuts, covered segments) and highlights the updated destination. - Given active assistive tech, When the reroute occurs, Then an aria-live announcement communicates the new bay and updated first maneuver (e.g., "Bay changed to 4. New first step: continue 100 ft then turn left to ramp"). - Given analytics, When a reroute is applied, Then a reroute event is logged with previous bay, new bay, and time-to-notify metrics.
Accessible Path Fidelity (Curb Cuts, Ramps, Covered Walkways)
- Given available accessibility graph data, When a route is generated, Then the path avoids stairs and non-ramped curbs and prefers curb cuts and ramps. - Given covered walkway data, When both uncovered and covered options exist, Then the covered path is chosen if the additional distance ≤ 20 meters; otherwise the shortest accessible path is chosen. - Given step rendering, When steps are displayed, Then each step includes an accessibility icon (ramp, curb cut, covered) and distance estimate accurate to ±10%. - Given conflicting data, When accessibility data is incomplete, Then steps are labeled accordingly (e.g., "accessibility unknown"), and no stairs-only segment is used without an explicit warning step.
Resilience: Low Connectivity and Fallbacks
- Given map tiles fail to load within 2 seconds or the device is offline, When the directions view is opened, Then a text-only step list with bearings and distances is shown with functional navigation controls. - Given intermittent connectivity, When requests fail, Then exponential backoff retries occur up to 3 times and the UI remains responsive with a visible, non-blocking status. - Given asset caching, When the page is revisited, Then critical assets are served from cache and the last known route is displayed with a "may be outdated" banner. - Given permission denial for location, When location is not granted, Then the experience still works with static distances from origin and clearly indicates "distance estimates based on starting point".
Bay‑Aware Instant Reroute
"As a guest en route, I want my directions to update instantly if my assigned bay changes so that I always follow the shortest accessible path to the new location."
Description

Automatically recalculate and update the guest’s route when a bay assignment changes or a bay is blocked, ensuring the guidance always targets the current best bay and entrance. Subscribe to real‑time assignment events from the staff dashboard and recompute the route with accessibility constraints preserved. Update the live link in-place without reload, show a clear visual/voiceover announcement of the new bay, and optionally send a concise SMS summary. Provide graceful degradation when GPS is unavailable by routing from the last known position or a selectable landmark. Outcome: guests are redirected immediately to the correct, accessible location, preventing confusion and delays.

Acceptance Criteria
Instant Reroute on Bay Reassignment
Given a guest has an active RampRoute guidance session with Bay N assigned and the guidance link open in a mobile browser When staff reassigns the guest to Bay M via the dashboard event stream Then the on-screen route updates in-place to target Bay M and the correct accessible entrance within 2 seconds of receiving the event And no full page reload occurs And existing map zoom and orientation are preserved And the recomputed path maintains accessibility constraints (ramps, curb cuts, covered zones; no stairs) And a banner displays “Bay M” prominently and the step list updates to reflect the new destination
Alternate Bay Selection When Current Bay Is Blocked
Given a guest is navigating to Bay N with active guidance And Bay N is marked blocked in the staff dashboard When the block event is published to the client Then the system selects the nearest eligible bay that satisfies accessibility constraints and capacity rules (closest by accessible path distance; tolerance ≤5%) And the route updates in-place within 2 seconds with the new bay and entrance And the UI displays the reason “Bay N blocked — rerouting to Bay M” And ETA and remaining distance update accordingly And the previous bay is no longer shown as a target anywhere in the UI
Guidance Continuity Without GPS Signal
Given device GPS permission is denied or reported accuracy is worse than 100 meters for ≥10 seconds When a bay change or block triggers a reroute Then the system uses the last known position if it is ≤5 minutes old to compute the route Else the guest is prompted to select a starting landmark from a predefined list, fully operable with screen readers And upon selection, the route recomputes from that landmark within 3 seconds And the reroute banner indicates the starting point used ("Routing from: [Landmark]")
Accessible Visual and Voice Announcement of New Bay
Given an active guidance session experiences a reroute to a new bay When the new bay and entrance are set Then an on-screen announcement banner appears within 1 second with 4.5:1 contrast, showing the new bay number and entrance And an ARIA live region (aria-live="assertive") announces the update and is read by VoiceOver/TalkBack within 1 second And a "Repeat announcement" control is visible, focusable, and replays the message on activation And no content auto-moves for more than 3 seconds without pause/stop controls And the banner remains for at least 8 seconds or until dismissed by the user
Concise SMS Summary on Reroute (Opt-in Only)
Given the guest has opted in to SMS summaries and has a verified mobile number When a reroute occurs due to bay reassignment or block Then exactly one SMS is sent within 10 seconds containing the new bay number, accessible entrance, and a short link to the live guidance And the message length is ≤160 characters and includes "Reply STOP to opt out" And guests without SMS opt-in receive no SMS And duplicate SMS messages are not sent for the same event
Real-time Event Subscription with Resilient Fallback
Given the guidance client is subscribed to real-time assignment events (WebSocket or SSE) When the real-time connection drops Then the client attempts exponential backoff reconnects (initial 1s, max 30s) And falls back to long-polling every 15 seconds until real-time reconnects And assignment changes that occur during outage are detected and applied within 20 seconds via polling And event processing is idempotent using monotonic event IDs so no duplicate reroutes occur And upon reconnection, missed events are fetched and applied in order
Accessibility Map Data Layer & Editor
"As a store manager, I want to define and maintain accurate ramp, curb cut, covered zone, and entrance data for my location so that guests receive reliable, accessibility‑aware guidance."
Description

Establish a geospatial data model for ramps, curb cuts, covered zones, pedestrian paths, entrances (including accessibility attributes), slopes/grades, and no‑go barriers. Provide a web editor for store setup and maintenance, import seed data from sources like OpenStreetMap, and add validation rules to ensure routable, ADA‑friendly paths. Support versioning, approvals, and rollback, and publish optimized tiles/GeoJSON to a CDN for fast client rendering. Outcome: a reliable, up‑to‑date foundation that enables accurate, accessibility‑aware routing per location.

Acceptance Criteria
Authoring Core Accessibility Features in Web Editor
Given I am an authenticated store setup user within a location boundary When I create features: Ramp (line), Curb Cut (point), Covered Zone (polygon), Pedestrian Path (line), Entrance (point), No‑Go Barrier (polygon/line) And I populate required attributes per schema And I snap features to connected nodes within 0.5 m And I save the changeset Then the save completes within 2 seconds And all features persist and re-render on reload And geometry validations pass: no self-intersections; polygons closed; paths connect within 0.5 m And schema validations pass: all required attributes present and typed And a routable graph is successfully built for the location
OpenStreetMap Seed Data Import and Mapping
Given an admin triggers OSM seed import for a location area ≤ 1 km² When the import job runs Then features are mapped from OSM tags: - kerb=lowered -> Curb Cut - highway=footway|path + ramp=yes -> Ramp - highway=footway|path -> Pedestrian Path - entrance=main|yes + wheelchair=yes -> Accessible Entrance - covered=yes|arcade|colonnade -> Covered Zone - barrier=wall|fence|guard_rail -> No‑Go Barrier And attribute mappings are populated where available (incline -> slope_percent, surface -> surface) And duplicates within 1.0 m with matching tags are merged And the job completes within 5 minutes And a report logs counts by feature type, merges, and failures And ≥ 95% of eligible OSM features are imported or updated
ADA-Friendly Routability Validation on Submit
Given a location dataset has pending edits When I submit the changeset for approval Then accessibility validations run and must pass: - From each active bay to ≥ 1 accessible entrance, an accessible path exists - Non-ramp path segments on accessible routes have slope_percent ≤ 5.0 - Ramp segments have ramp=true and slope_percent ≤ 8.33 - Routes do not cross No‑Go Barriers - Path-to-curb transitions occur only at Curb Cuts And failures block submission and list failing checks with feature IDs and reasons And passing validations produce a success summary with route coverage stats
Versioning, Approval Workflow, and Rollback
Given I save edits in the editor When a draft changeset is created Then a diff preview is available showing added/modified/deleted features And when I request approval Then users with Approver role can approve or reject with a required comment And only approved versions are marked current and eligible for publish And I can rollback to any prior approved version in one action without data loss And the audit log records user, timestamp, action (save/approve/reject/publish/rollback), and version IDs
CDN Publication of Optimized Tiles and GeoJSON
Given a version is approved When I publish the map data Then vector tiles (z12–z20) and a minified GeoJSON of the accessible network are generated And assets are uploaded to the CDN with ETag and Cache-Control: public, max-age=300 And CDN propagation completes ≤ 60 seconds at the 95th percentile And mean tile size ≤ 50 KB at z18 and ≤ 20 KB at z16 And client fetch latency from 3 test regions is ≤ 200 ms at the 95th percentile And the tile/GeoJSON endpoints return HTTP 200 and correct Content-Type
Reroute Uses Latest Published Accessibility Layer
Given a bay assignment changes for an active pickup When the system triggers an updated route Then the routing engine requests tiles/GeoJSON using ETag/If-None-Match And the latest published data is used to compute the route And the updated route is delivered to the guest within 3 seconds of the bay change And the route honors accessibility constraints (avoids No‑Go, uses Curb Cuts, respects slope limits) And if CDN assets are stale or 5xx, the system falls back to origin and retries once before alerting
Low‑Bandwidth & Text‑Only Fallback
"As a guest on a limited data connection, I want lightweight, text‑only directions with a simple map fallback so that I can still find the accessible path without heavy loading or delays."
Description

Provide progressive enhancement so directions work on weak connections: prioritize a compact text step list with landmarks, offer an auto‑generated static map image, and defer or skip heavy map tiles. Keep the JS/CSS payload small, cache aggressively, and support offline/spotty sessions. Ensure all information is readable by screen readers and usable without panning/zooming. Outcome: guests reliably receive clear, accessible guidance even with limited data or older devices.

Acceptance Criteria
Slow 3G Text‑First Rendering
Given a first-time visitor on a mid-tier Android device emulated with Slow 3G (400 kbps down/400 ms RTT) and an empty cache When the SMS deep link to RampRoute Directions is opened Then a compact, numbered text step list with landmarks renders above the fold within 2.5 seconds And the combined initial HTML+CSS+JS transfer size is <= 60 KB gzipped And no map tile requests are made before any user interaction And the page is usable without horizontal scrolling at 320 px viewport width
Static Map Fallback Displays When Tiles Deferred
Given network quality is detected as Slow 3G or the user has Data Saver enabled When directions are displayed Then a static map image (PNG/WebP) of the route is shown in place of interactive tiles And the image payload is <= 120 KB and fits the viewport without requiring panning or zooming And the image includes descriptive alt text summarizing the path, bay number, and accessible entrance And if the image fails to load, the text step list remains visible and complete
Deferred Interactive Map Loading
Given the initial directions screen on any connection quality When the page loads Then interactive map tiles are not requested until the user explicitly taps "Load interactive map" And the control discloses an estimated data size before loading And at most 0 tile requests occur prior to user action And after opt-in, no more than 8 tiles are requested for the initial viewport
Offline and Spotty Connectivity Support
Given the user opened the SMS link at least once online and a service worker is registered When connectivity is lost or intermittent thereafter Then the cached text steps and latest static map for that arrival token load within 1.5 seconds And navigation between steps works without network access And a non-blocking banner states "Offline — showing last updated directions" with a last-updated timestamp And when connectivity resumes, content refreshes within 3 seconds and the banner updates to confirm the refresh
Screen Reader and Keyboard Accessibility
Given a screen reader (TalkBack/VoiceOver/NVDA) or keyboard-only navigation is used When the directions page loads Then the step list is exposed as an ordered list with each step a listitem including landmark cues in text And reading order matches visual order with no focus traps And all controls are keyboard reachable with visible focus indicators and logical tab order And an aria-live="polite" region announces reroutes or bay changes in plain language And contrast ratios are >= 4.5:1 and touch targets are >= 44x44 CSS px And the page is fully usable without pinch-zoom at 320 px width with a base font size >= 16 px
Resource Budget and Repeat-Load Performance
Given the directions page is built with production optimizations When tested on Slow 3G with a cold cache Then total JS payload is <= 30 KB gzipped and CSS <= 15 KB gzipped And no third-party tracking scripts load on the directions view And First Contentful Paint (FCP) <= 1.8 seconds and Time to Interactive (TTI) <= 3.0 seconds When tested with a warm cache Then transfer size on reload is <= 10 KB and FCP <= 1.0 second
Low-Bandwidth Bay Change Reroute
Given a bay change is triggered while the user has the directions page open on Slow 3G When the update is sent Then only a delta payload <= 5 KB is fetched to update the step list and static map URL And the updated steps reflecting the new bay and accessible path are visible within 2 seconds And an aria-live polite announcement states "Bay changed to {n}. Steps updated." And no interactive map tiles are requested as part of the update
Multimodal Access Profiles & Preferences
"As a guest with mobility preferences, I want to choose an access profile so that my route matches my needs."
Description

Allow guests to select an access profile (e.g., wheelchair/low‑mobility, stroller‑friendly, avoid stairs, stay under cover) via simple toggles in the SMS link. Respect profiles in the routing engine by applying constraints such as no stairs, max slope, curb‑cut availability, and covered‑path bias. Support defaulting from order notes or saved preferences and visibly indicate when a step deviates from preferences (with alternatives). Outcome: routing adapts to individual mobility needs, improving safety and comfort.

Acceptance Criteria
Profile Selection via SMS Link Toggles
Given a guest opens the RampRoute SMS link on a mobile browser When the page loads Then four toggles are visible and labeled: “Wheelchair/Low‑mobility”, “Stroller‑friendly”, “Avoid stairs”, “Stay under cover” And each toggle is off by default unless pre‑populated by saved preferences or order notes And the page becomes interactive within 2.0 seconds on a 3G Fast profile. Given a guest changes any toggle When the change is made Then the selection is applied to routing and a new route is displayed within 1.0 second And the selection is stored for the current session. Given a screen reader user When navigating the toggles Then each toggle exposes role="switch", an accessible name, and state via ARIA And keyboard focus order matches visual order And automated accessibility scan reports 0 critical/serious violations for the toggle region (axe-core).
Wheelchair/Low‑Mobility Constraints Enforcement
Given the “Wheelchair/Low‑mobility” toggle is ON When a route is generated Then the route contains 0 stairs/steps segments And all curb transitions have curb cuts available And the maximum segment slope is ≤ 8.3% grade And surfaces with gaps > 1.3 cm or step-ups > 2 cm are excluded when data is available. Given no route satisfies all constraints When routing completes Then the system selects the route minimizing violations (priority: no stairs > curb cuts > slope) And marks each violated step as a deviation with reason and measured value (e.g., “Slope 9.5%”).
Avoid Stairs & Covered Path Bias
Given the “Avoid stairs” toggle is ON When a route is generated Then the route contains 0 stairs/steps segments; if impossible, a deviation badge is shown and the count of stair steps is displayed. Given the “Stay under cover” toggle is ON When multiple viable routes exist Then a covered route is chosen if its total distance is within +15% of the shortest uncovered route Else the chosen route maximizes covered distance percentage and displays the covered percentage. Given both toggles are ON When routing completes Then both constraints are applied concurrently using the same thresholds.
Defaulting From Order Notes and Saved Preferences
Given an order note includes recognized indicators (e.g., “wheelchair”, “stroller”, “avoid stairs”, “under cover”) or structured tags When the SMS link is opened Then matching toggles are pre‑selected And a banner explains the source (“Applied from your order notes”) with an option to change. Given the phone number has a saved profile When the SMS link is opened Then saved preferences pre‑populate the toggles and override order‑note defaults And the source banner states “Applied from your saved preferences”. Given the guest updates toggles and taps Save When the session ends Then preferences are saved to the phone number only if the guest has opted in previously or does so now; otherwise session‑only. Given both sources exist When pre‑population occurs Then precedence is: Saved preferences > Order notes > Defaults (off).
Deviation Indicators With Accessible Alternatives
Given any selected preference cannot be fully satisfied in one or more steps When the route is displayed Then each non‑conforming step shows a visible deviation badge with a reason code and metric (e.g., “Uncovered 18 m”, “No curb cut”, “Slope 9.5%”). Given at least one alternative exists that removes the deviation When the guest taps “See alternative” Then an alternative path is shown that satisfies the preference with cost deltas (distance/time) indicated And alternatives are only offered if the added distance ≤ 30% or added time ≤ 3 minutes, whichever is lower. Given no compliant alternative exists When the route is displayed Then the UI states “No fully accessible alternative available” and offers “Request assistance” (click-to-call/SMS) without blocking navigation. Then automated accessibility scan reports 0 critical/serious violations for deviation and alternative controls (axe-core).
Bay Change Reroute Preserves Preferences
Given a guest has selected any access toggles and has an active route When the assigned bay changes or is re-assigned by staff Then the route is recalculated and displayed within 1.0 second And all previously selected preferences remain applied without user action And a non‑modal toast summarizes the change and any new deviations. Given the reroute introduces new deviations When the new route is shown Then deviation badges and reasons update accordingly And the guest can open alternatives for the affected steps. Given network loss occurs during reroute When connectivity returns within 30 seconds Then the reroute resumes automatically with preferences intact; otherwise a retry control is offered.
Staff Zone & Bay Configuration Sync
"As curbside staff, I want to mark bays or paths as temporarily closed and have routing respect those changes in real time so that guests are not directed to blocked or unsafe areas."
Description

Enable staff to manage bays, temporary closures, hazards, and weather‑dependent covered zones from the dashboard and broadcast these changes to the routing service in near real time (<2 seconds). Ensure changes are atomic, auditable, and immediately reflected in both new routes and in‑progress guidance. Provide safeguards against directing guests to blocked or unsafe areas and automatically propose alternative accessible bays. Outcome: the guidance remains accurate as on‑the‑ground conditions change.

Acceptance Criteria
Near-Real-Time Dashboard-to-Routing Sync
Given a staff member updates a bay/zone status and clicks Save When the dashboard confirms the save Then the routing service receives and acknowledges the new configuration within 2 seconds at p95 and 5 seconds at p99 And the routing service exposes the updated configuration with a new version ID And subsequent route computations reference the new version ID
Atomic Multi-Field Configuration Update
Given multiple configuration fields are edited in one action (e.g., close Bay 3, mark Hazard=Ice, enable Covered Zone A) When the update is saved Then consumers see either the entire previous version or the entire new version, never a partial mix And the change is committed as a single transaction with one monotonically increasing version ID And if any write/broadcast step fails, the update is rolled back with no version change and a clear error is surfaced to the user
Instant Reroute for In-Progress Guidance
Given a guest is actively following directions to an assigned bay And that bay is changed to Unavailable or Hazardous When staff save the change Then the guest’s active session receives a reroute event within 2 seconds And the updated guidance avoids the unavailable/hazardous areas and leads to a valid bay And the guest UI clearly indicates the bay change and updated path without requiring app reload
Safety Guardrails Against Blocked/Unsafe Areas
Given any zone, path segment, or bay is flagged as Blocked or Unsafe When generating directions Then no route includes the flagged elements And if all accessible routes are unavailable, the system instructs the guest to wait and automatically alerts staff within 2 seconds of the attempt And the dashboard displays a persistent warning until the condition is cleared by staff
Automatic Alternative Accessible Bay Suggestion
Given the assigned bay becomes unavailable due to closure or hazard When computing an alternative Then the system selects the nearest available bay that maintains an obstacle-free, ramp-enabled path And if Weather=Precipitating, covered bays are preferred when distance penalty is ≤50 meters compared to the nearest uncovered accessible bay And the suggested alternative and selection rationale are recorded and surfaced to both staff and guest
End-to-End Auditability and Traceability
Given any configuration change is saved When auditing the system Then an immutable log entry exists with actor identity, timestamp (UTC), before/after values, reason code, impacted bays/zones, and version ID And audit entries are filterable by time range, user, and bay/zone, and exportable to CSV And audit retention is at least 365 days and entries are tamper-evident via chained hashes or signatures
Concurrency Control and Conflict Resolution
Given two staff members edit the same bay/zone configuration concurrently When the second user attempts to save with a stale version Then the system rejects the save with a 409 Conflict and prompts the user to reload the latest configuration And no partial updates occur and the first committed change remains intact And both attempts are logged with correlation IDs for traceability

BayGuard Reserve

Protects a small pool of closest, wider, or covered bays for accessibility‑flagged arrivals. RightBay AI and BaySwap honor these holds, gently redirecting other guests to alternatives. Ensures priority bays stay available when they’re needed most, cutting lot circling and keeping handoffs timely.

Requirements

Accessibility Flag Capture via SMS Flow
"As a guest with accessibility needs, I want to indicate my need for a closer, wider, or covered bay so that I am reliably assigned an appropriate bay on arrival without extra steps."
Description

Enable guests to self-identify accessibility needs (closer, wider, or covered bay) within the existing SMS-driven “I’m here” browser flow with one-tap options. Support session-only flags and optional, consented number-level persistence. Ensure WCAG-compliant UI, low-friction defaults, and multilingual copy. Propagate the flag to the order context, trigger BayGuard Reserve holds, and expose the flag to RightBay AI and Ops Dashboard in real time. Include staff-side ability to mark an arrival as accessibility-flagged post hoc. Respect privacy (no medical details), log events for analytics, and handle third-party marketplace orders where customer data is limited.

Acceptance Criteria
One-Tap Accessibility Selection in SMS "I'm here" Flow
Given a guest opens the "I'm here" link from the SMS on a mobile browser When the arrival page loads Then the page displays one-tap options for "Closer," "Wider," and "Covered" bays plus a "No accessibility preference" default Given the options are displayed When the guest selects one or more accessibility options and taps Continue Then the selection is recorded for the current arrival session and reflected in the order pre-submit summary Given the guest takes no action on the options When they tap Continue Then "No accessibility preference" is submitted by default Given a first-time guest on a 3G-equivalent mobile connection When the arrival page loads Then time to interactive for the options is <= 2 seconds
Consent-Based Number-Level Preference Persistence
Given a guest selects an accessibility option When a "Remember for next time" toggle is enabled and the guest taps Continue Then the selection and explicit consent are persisted to the guest's phone number as a preference Given the same phone number starts a new arrival within the default retention window (90 days, configurable) When the arrival page loads Then the previously consented accessibility options are pre-selected and clearly labeled as remembered Given the guest opens "Manage preference" When they revoke consent Then the stored preference is deleted within 60 seconds and future arrivals default to "No accessibility preference" Given the guest's number is masked or transient from a third-party marketplace When the arrival page loads Then persistence controls are hidden or disabled and only session-only selection is available
WCAG 2.1 AA Compliance for Accessibility Options UI
Given the accessibility options UI When audited with axe-core and manual checks Then there are 0 serious/critical violations and all interactive controls have accessible names, roles, and states Given the page is zoomed to 200% When tested on iOS Safari and Android Chrome Then no content or functionality related to the options is lost and horizontal scrolling is not required Given color and contrast settings When measuring text and iconography in the options Then contrast ratios meet or exceed 4.5:1 for normal text and 3:1 for large text/icons Given a user navigates with keyboard or switch control When moving focus through the options Then every control is reachable and actionable with a visible focus indicator and logical focus order
Multilingual Copy and Fallback for Accessibility Options
Given a store supports English and Spanish When the arrival page detects Accept-Language or a locale parameter in the link Then the accessibility options and related copy render in the highest-ranked supported locale or fall back to English Given a guest taps "Change language" When they select a supported language Then the page updates immediately and the choice persists for the current session Given SMS updates are sent about bay assignment or changes due to accessibility When messages are delivered during the session Then SMS content matches the session language
Real-Time Propagation to Order Context and BayGuard Reserve Holds
Given a guest submits an accessibility-flagged arrival When the submission is received by the backend Then the order context is updated with flag values within 1 second and an event is emitted Given the order context contains an accessibility flag When BayGuard Reserve evaluates bay availability Then at least one matching bay is held within 2 seconds and non-flagged guests are not assigned to that reserved pool Given an accessibility flag is present When RightBay AI selects a bay Then it chooses from the reserved pool or triggers BaySwap to free one if none are open Given staff are viewing the Ops Dashboard When the guest submits the accessibility-flagged arrival Then an accessibility badge appears on the order card within 2 seconds and bay assignment respects the reserved pool Given a non-flagged guest attempts to select or is auto-assigned a reserved bay When assignment is executed Then the system redirects them to an alternative bay with explanatory copy and without blocking completion
Staff Post-Hoc Accessibility Marking in Ops Dashboard
Given an active arrival without an accessibility flag When a staff member toggles "Mark accessibility needed" and selects one or more options Then the order context updates immediately, BayGuard Reserve re-evaluates holds, and RightBay AI is notified in real time Given a bay is already assigned that does not meet the newly marked need When the flag is applied Then BaySwap attempts reassignment within 5 seconds and the guest receives updated bay instructions via SMS Given staff perform post-hoc marking When audit logs are inspected Then an entry exists with staff ID, timestamp, action, selected options, and order ID
Privacy Boundaries and Event Analytics for Accessibility Flags
Given the accessibility selection UI When inspecting available inputs Then no free-text fields are present and no medical details are collected; only enum values {Closer, Wider, Covered}, consent boolean, language, and session metadata Given session-scoped storage When 24 hours elapse or the order completes Then session accessibility flags are deleted Given number-level persistence is enabled When data is stored Then the phone number is stored as a salted hash; raw numbers are excluded from analytics; staff-facing views show only an icon and selected need types Given analytics is enabled When a guest views options, selects options, toggles consent, submits arrival, reserve is triggered, or staff marks/unmarks Then events {option_viewed, option_selected, consent_changed, arrival_submitted, reserve_triggered, staff_marked} are recorded with orderId, storeId, timestamp, and phone_hash where available, achieving >= 99% delivery within 60 seconds Given a data deletion request for a phone number When processed Then any persisted accessibility preference for that number is deleted within 7 days and future arrivals default to "No accessibility preference"
Reserved Bay Pool Configuration & Scheduling
"As an operator, I want to configure which bays are reserved and when so that priority bays remain available for accessibility arrivals during the times they are most needed."
Description

Provide location-level controls to define a configurable pool of reserved bays by attribute (closest, wider, covered), count, and specific bay numbers. Support daypart schedules, service hours, and blackout windows; validate against total bays and signage mappings. Allow policy rules such as minimum reserved count, dynamic caps, and per-channel overrides (web, phone, marketplace). Store configuration in a versioned, API-addressable model with safe defaults and guardrails, and propagate changes to assignment services with zero-downtime updates.

Acceptance Criteria
Configure Reserved Bays by Attribute, Count, and Specific Numbers
Given a location with mapped bays and attribute tags (closest, wider, covered) When an admin sets reserved counts per attribute and selects explicit bay numbers Then the system deduplicates overlaps, restricts to mapped bays, and composes the final reserved set And Save is enabled only when attribute constraints and counts are consistent And on Save a new monotonically increasing versionId is created and persisted And GET /locations/{id}/bay-guard/config returns counts, attributes, explicit bay numbers, versionId, and updatedAt
Validation Against Total Bays and Signage Mappings
Given total bays and a signage map of valid bay numbers When reserved counts exceed total bays or explicit bay numbers include unmapped or duplicate entries Then Save is blocked with HTTP 422 and field-level errors (BAY_COUNT_EXCEEDS_TOTAL, BAY_NOT_MAPPED, DUPLICATE_BAY) And the last published version remains active and unchanged
Daypart Scheduling, Service Hours, and Blackout Windows
Given configured dayparts, service hours, and blackout windows When the current time falls within a defined daypart and service hours Then the effective reserved pool is the policy for that daypart And when the current time falls within a blackout window Then the effective reserved pool is 0 regardless of daypart And GET /locations/{id}/bay-guard/config?at=ISO8601 returns the resolved policy for that timestamp
Policy Rules: Minimum Reserved Count and Dynamic Caps
Given minReserved=N and dynamicCap=ceil(pct × activeBayCount) When activeBayCount changes (e.g., bays marked offline/online) Then reservedCount recalculates to max(minReserved, dynamicCap) and never exceeds activeBayCount And changes take effect at the next assignment evaluation tick and are reflected in the effective policy API
Per-Channel Overrides (Web, Phone, Marketplace)
Given a default policy and per-channel overrides When an arrival is identified for a specific channel Then the assignment services apply that channel’s reserved policy; if none exists, they apply the default And the effective channel policy is returned in API responses and logged with appliedChannel in assignment logs
Versioned Config and Zero-Downtime Propagation
Given version vX is live When a new configuration vY is saved Then assignment services receive and begin using vY within 3 seconds p95 for new assignments And in-flight assignments continue under vX until completion (no reassignment) And if vY fails health checks within 60 seconds, the system rolls back to vX and emits an auditable rollback event
Assignment Enforcement in RightBay with BaySwap Integration
"As a shift lead, I want the bay assignment engine to respect reserved bays and automatically swap assignments when needed so that accessibility guests receive priority without manual juggling."
Description

Extend RightBay AI to honor reserved pools by allocating from them only for flagged arrivals, excluding them for others. Implement ETA lookahead to pre-hold bays within a configurable window and predict conflicts. Integrate BaySwap to proactively swap non-flagged assignments out of reserved bays when a flagged arrival is inbound, minimizing disruption with clear reassignment logic and fairness constraints. Provide deterministic tie-breakers, manual override safety, and graceful degradation when the reserved pool is saturated, including alternate suggestions.

Acceptance Criteria
Flagged Arrival Uses Reserved Pool
Given an arrival is marked accessibility-flagged and at least one reserved bay is free And the bay is not pinned by a manual override When RightBay assigns a bay Then the assignment is from the reserved pool And no non-reserved bay is selected while a reserved bay is available And the assignment record persists isReserved=true and reason=reserved_pool
Non-Flagged Excluded From Reserved Bays
Given an arrival is not accessibility-flagged And there is at least one non-reserved bay available When RightBay assigns a bay Then no reserved bay appears in the candidate set And the selected bay has isReserved=false And the decision log includes exclusionReason=reserved_for_accessibility
ETA Lookahead Pre-Hold and Conflict Prediction
Given a flagged order with predicted ETA within the pre-hold window W=8 minutes When the ETA first enters the window Then RightBay pre-holds one reserved bay and marks status=prehold with expiresAt=ETA+B where B=2 minutes And if ETA changes by >60 seconds, the pre-hold updates within 5 seconds And no more than one pre-hold exists per flagged order at any time And if the pre-held bay is occupied by a non-flagged guest whose estimated completion time > (flagged ETA − B), a conflict is created with severity=high and BaySwap is queued And the pre-hold auto-releases on cancel or if no check-in occurs by ETA+B
BaySwap Execution for Incoming Flagged Arrival
Given a flagged arrival is within W=8 minutes and all reserved bays are occupied by non-flagged guests And at least one non-reserved alternative bay exists When BaySwap runs Then the system selects the occupying non-flagged guest with the lowest disruptionScore = handoffDelaySeconds + 0.5*distanceDeltaMeters And reassigns that guest to the best available non-reserved bay within 2 seconds And the displaced guest receives a notification with reason="Accessibility priority" and the new bay And the flagged guest is assigned the vacated reserved bay And no guest is displaced more than once per visit And the same guest is not swapped again within 10 minutes
Deterministic Tie-Breakers for Assignment and Swap
Given multiple candidates have equal computed scores for assignment or swap When a selection must be made Then RightBay applies tie-breakers in order: 1) For assigning a reserved bay: choose the lowest-numbered reserved bay 2) For selecting a displacement candidate: choose the one with the latest estimated completion time 3) If still tied: choose the lexicographically smallest orderId And with identical inputs the algorithm produces identical selections across runs
Manual Override Safety and Audit
Given staff attempts a manual override affecting any reserved bay When the override would place a non-flagged guest into a reserved bay and a flagged ETA is within W=8 minutes Then the system blocks with a warning and requires manager confirmation And if confirmed, the override pins the bay for P=10 minutes and pauses BaySwap for that bay during the pin And all overrides create an audit record capturing userId, timestamp, beforeBay, afterBay, reason, brokePolicy=true|false And automated assignment respects the pin and resumes normal behavior after P expires
Graceful Degradation and Alternative Suggestions
Given the reserved pool is saturated and no eligible BaySwap candidate exists When a flagged arrival is within W=8 minutes or checks in Then RightBay does not assign a reserved bay but suggests the two nearest non-reserved bays by distance And sends guidance via SMS/web with a map link and staff alert within 3 seconds And places the flagged arrival at the top of the next-release queue for reserved bays And includes an estimated wait time with ±1 minute accuracy And emits a metric event bayguard.degraded=true with context
Soft Redirect Guidance for Non-Flagged Arrivals
"As a non-flagged guest, I want clear, timely guidance to an alternative bay so that I can park quickly without occupying a reserved space."
Description

Deliver context-aware, friendly SMS and in-browser prompts that steer non-flagged guests away from reserved bays toward the best available alternatives. Include bay number suggestions, simple map snippets, and signage cues. Provide copy variants for first-time vs. returning guests, multi-language support, and escalation messaging when someone occupies a reserved bay. Ensure updates reflect live reassignment events, maintain brand voice, and avoid friction that could cause abandonment.

Acceptance Criteria
SMS Soft Redirect on Arrival Near Reserved Bays
Given a non-flagged guest triggers an arrival ping within the geofence And BayGuard Reserve has one or more nearby bays on hold When the system selects an alternative bay Then an SMS is enqueued within 500 ms and attempted within 3 seconds And the SMS includes exactly: a suggested non-reserved bay number, a signage cue (e.g., color/marker), and a short map link with the bay pinned And the suggested bay is not part of the reserved pool at send time And the message length is <= 320 GSM-7 characters (or <= 160 UCS-2 if required) And the copy tone is friendly/neutral and contains no blame language And a 1-tap “I’m parked” confirmation link is present And the redirect decision (reserved bays present, suggested bay, timestamp) is logged for audit
In-Browser Prompt Redirect After 'I'm Here' Click
Given a non-flagged guest taps the in-browser “I’m here” link And reserved bays exist within 50 meters of the entrance When the arrival page loads Then a soft-redirect banner renders within 1 second with the recommended non-reserved bay number And an inline mini-map (static or lightweight tile) displays the bay pin with accuracy ≤ 10 meters And signage cues (color/letter/row) are displayed And reserved bay numbers are hidden from suggestions and lists And a “Directions” link opens mapping with the bay coordinates prefilled And the UI is accessible (ARIA labels present, contrast AA+, keyboard operable)
Copy Variants for First-Time vs Returning Guests
Given guest identity can be resolved to first-time or returning based on order history or cookie When generating SMS and in-browser copy Then first-time guests receive instructional copy with brief context and signage explanation And returning guests receive concise copy with minimal guidance And variant selection accuracy is ≥ 99.5% over a rolling 7-day window And both variants comply with brand voice rules: reading grade ≤ 8, greet + thank-you present, no blame/shame lexicon And variant choice is included in analytics events
Multi-Language Redirect Messaging
Given a guest has a preferred language on file or sends an arrival from a browser with Accept-Language headers When composing SMS and in-page prompts Then copy is delivered in the preferred supported language when available (min: English, Spanish) And if unsupported, fallback is English with an inline “Change language” option And dynamic tokens (bay number, distance, directions verbs) are localized And diacritics render correctly, and message stays within the character limits for the chosen encoding And the map snippet labels match the selected language where supported
Polite Escalation When Reserved Bay Is Occupied
Given a non-flagged guest confirms parking in, or staff marks them occupying, a reserved bay When the system detects this state Then a courteous escalation SMS is sent within 30 seconds asking the guest to move, with the nearest non-reserved bay suggested And the message offers a one-tap “Need accessibility?” self-identification option that updates their flag immediately if selected And if no move confirmation is received within 3 minutes, staff are alerted with guest/order/bay details And no more than 2 escalation messages are sent per arrival And all actions (detection source, messages, outcomes) are logged
Live Reassignment Updates to Guidance
Given RightBay AI or BaySwap reassigns the best bay before the guest confirms “I’m parked” When a reassignment event occurs Then the in-browser guidance updates within 1 second without page reload And a single follow-up SMS is sent within 2 seconds reflecting the new bay And only the latest recommendation is visible; prior suggestions are visually retired And updates are rate-limited to ≤ 1 per 30 seconds per guest to avoid spam And if the guest has already confirmed parked, no reassignment SMS is sent (UI shows status-only) And the event is logged with reason code
Friction Minimization and Anti-Abandonment
Given a non-flagged arrival is being guided away from reserved bays When sending guidance Then total outbound messages per arrival are capped at 2 (excluding mandatory escalations) And SMS contains at most one link and no attachments > 50 KB And time-to-first-bay-suggestion is ≤ 2 seconds (page) and ≤ 3 seconds (SMS) And a visible “Skip guidance / I’ll choose” option is present and functional And STOP/HELP keywords are honored immediately with compliance logging And abandonment (no interaction within 5 minutes) triggers a single gentle nudge or staff notification per configuration
Reclaim, Overflow, and Preemption Logic
"As a manager, I want idle reserved bays reclaimed and temporarily utilized with preemptive alerts so that capacity is maximized without compromising accessibility."
Description

Implement timers and policies to release reserved bays if a flagged arrival is late (grace window), and allow temporary use by non-flagged guests with instant preemption if a flagged arrival nears. Support a lightweight waitlist for flagged arrivals when the reserved pool is full, with proactive alerts and ETAs. Provide audible/visual cues in the Ops Dashboard, write audit events for all state changes, and ensure fairness and predictability to keep throughput high while protecting accessibility guarantees.

Acceptance Criteria
Grace Window Reclaim of Reserved Bay for Late Flagged Arrival
Given a flagged order has an active reserved bay hold and graceWindowMinutes = G is configured And the customer has not checked in via the “I’m here” link and no arrival is auto-detected When currentTime >= scheduledPickupTime + G Then the system changes the bay status from Held-Flagged to Available And the reservation state for the order becomes Reclaimed-Late with reclaimReason = "GraceExpired" And any pending non-flagged assignments may now consider this bay per assignment policy And the customer receives an SMS prompting to check in when near if notifications are enabled
Temporary Use of Reserved Bay by Non-Flagged Guest with Instant Preemption
Given all non-reserved bays are occupied and at least one reserved bay is idle And preemptionTriggerSeconds = P and swapSLASeconds = S are configured When a non-flagged customer checks in Then the system may assign an idle reserved bay with assignmentType = Temporary-NonFlagged And upon detection of any flagged arrival with ETA <= P or distance <= configuredPreemptionRadius Then the system initiates preemption and within S seconds And the non-flagged customer is reassigned to the best available non-reserved alternative (or queue if none) And the flagged arrival’s reserved bay is marked Held-Flagged with preemptionReason = "FlaggedIncoming" And both parties receive appropriate SMS/ops notifications reflecting the change
Flagged Waitlist Creation and Auto-Assignment with Proactive Alerts
Given the reserved bay pool is fully occupied or held and a flagged customer checks in And waitlist is enabled with etaUpdateIntervalSeconds = U and escalationThresholdMinutes = E When the flagged customer cannot be immediately assigned Then the system adds the customer to the flaggedWaitlist with position calculated by FIFO and configured priority rules And the Ops Dashboard displays the customer’s waitlist position and predicted ETA And the customer receives an SMS confirming waitlist placement with current ETA And ETA and position updates are pushed at most every U seconds or on material change And when a reserved bay becomes available, the top-of-waitlist flagged customer is auto-assigned within U seconds And if wait time exceeds E, an escalation alert is sent to ops per policy
Ops Dashboard Audible/Visual Cues for Reclaim and Preemption Events
Given Ops Dashboard is open with sound enabled When a reclaim is executed, a preemption is imminent, a preemption is executed, or a waitlist position changes Then a distinct visual badge (color/icon) is shown for each event type within 1 second of event time And a non-intrusive audible cue plays once per event type occurrence (debounced within 10 seconds) And operators can acknowledge/dismiss the cue; acknowledged items remain visible in history for at least 30 minutes And cues meet WCAG AA contrast and provide a text alternative describing the event
Comprehensive Audit Logging for Reservation State Changes
Given any state change related to BayGuard Reserve (hold, reclaim, temporary assignment, preemption start/complete, waitlist add/remove, ETA update) When the state change occurs Then an audit event is written within 500 ms including: eventType, orderId, bayId (if any), fromState, toState, reasonCode, actor (system/user), timestamp (UTC), and correlationId And events are immutable, append-only, and retrievable via the audit API filtered by time range, orderId, or bayId And duplicate processing results in idempotent events (no duplicate logical entries for same correlationId) And a failure to write the audit event is surfaced as a warning in Ops Dashboard and retried per backoff policy
Fairness and Predictability for Non-Flagged Guests Under Preemption
Given preemptionLimitPerOrderInWindow = L and preemptionWindowMinutes = W are configured When a non-flagged order has been preempted L times within W minutes Then the system will not preempt that order again within the same window unless no other non-flagged alternatives exist And reassignment chooses the nearest-time-to-ready non-reserved bay per RightBay policy to minimize added wait And each preemption includes a human-readable reason shown to ops and included in customer messaging And median reassignment completion time for preempted orders is <= configuredReassignSLASeconds over a 1-hour rolling window
Simultaneous Flagged Arrivals and Deterministic Tie-Breaking
Given fewer reserved bays are available than the number of flagged arrivals with ETA <= preemptionTriggerSeconds When multiple flagged arrivals compete for the remaining reserved bays Then assignment priority is determined by: 1) earliest check-in time, 2) soonest ETA, 3) lowest orderId as deterministic tiebreaker And only the top N arrivals (where N = available reserved bays) receive immediate holds; others enter the flagged waitlist And no bay is reassigned more than once within minReassignIntervalSeconds to avoid thrashing And all involved orders receive notifications indicating assignment outcome and expected next action
Ops Dashboard Visibility and Overrides
"As a shift lead, I want clear visibility and quick overrides for reserved bays so that I can resolve edge cases and keep handoffs on time."
Description

Add real-time indicators for reserved bay occupancy, upcoming flagged ETAs, countdowns, and conflict risk. Provide one-click actions to reassign bays, temporarily expand or shrink the reserved pool, and mark exceptions with required reason codes. Enforce role-based access, surface keyboard shortcuts, and ensure mobile-friendly layouts for curbside runners. All overrides are logged with timestamps and user IDs for accountability.

Acceptance Criteria
Real-time Reserved Bay Indicators
Given BayGuard Reserve is enabled and the Ops Dashboard is open When any bay’s status or reservation state changes Then the reserved-bay occupancy count and per-bay badges update on all connected dashboards within 3 seconds average and 5 seconds p95 Given reserved bays exist When viewing the bay grid Then reserved bays are visually distinguished with a unique badge and color, and tooltips display “Reserved for accessibility” with the current hold reason Given there are N reserved bays and R currently occupied When the header chip is rendered Then it displays “Reserved: R/N” and matches the grid state Given the dashboard is viewed on a mobile device ≤414px width When the bay list renders Then the reserved status is shown as a compact chip above the list and per-bay reserved badges remain legible with text ≥14px and contrast ≥4.5:1
Upcoming Flagged ETA Countdowns
Given an accessibility‑flagged order has ETA within the next 15 minutes When the dashboard is open Then a countdown timer in mm:ss is displayed in the Flagged Arrivals panel and on the assigned bay card Given the countdown reaches 03:00 When the timer updates Then the badge color switches to warning; when it reaches 00:30 it switches to danger Given the guest taps “I’m here” When arrival is detected Then the countdown stops and the badge switches to Arrived within 2 seconds Given the ETA changes by SMS or staff edit When the new ETA is saved Then the countdown recalculates within 3 seconds and the change is logged
Conflict Risk Alerts
Given projected accessibility‑flagged arrivals in the next 10 minutes exceed reserved bays expected to be free When the calculation runs each minute Then a Conflict risk banner appears with severity Low for deficit 1, Medium for 2–3, High for ≥4 Given a Conflict risk banner is shown When the banner renders Then it includes one‑click actions Expand reserved pool and Preemptive reassign and displays the impacted time window Given the deficit returns to ≤0 When conditions are recalculated Then the banner auto‑clears within 5 seconds
One‑Click Bay Reassignment Honors BayGuard
Given a user with role Dispatcher or Manager selects an order and clicks Reassign When choosing a target bay that is in the reserved pool and the order is not accessibility‑flagged and holds exist for flagged arrivals within 10 minutes Then the reassignment is blocked and the system suggests the top 3 alternative bays ranked by RightBay AI Given the target bay is not reserved or no upcoming flagged arrivals are impacted When the user confirms reassignment Then the reassignment completes within 500 ms and updates all clients within 3 seconds Given a user attempts to force‑assign into a reserved bay When the override modal opens Then a reason code is required and only a Manager can proceed Given keyboard shortcuts are enabled When the user presses R on a selected order Then the system reassigns to the top AI suggestion; when Shift+R is pressed it opens the full picker
Reserved Pool Resize Controls with Constraints
Given a Manager opens BayGuard Reserve controls When +Reserve or −Reserve is clicked Then the reserved pool size changes by 1 within 3 seconds across the UI, constrained to 0 ≤ reserved ≤ total bays Given an expansion is applied When a duration is required Then a value between 5 and 120 minutes must be selected (default 30) and on expiry the pool auto‑reverts with a toast notification Given a shrink would evict currently assigned flagged arrivals When the user attempts to shrink Then the action is blocked and an explanation is shown Given a non‑Manager attempts to change the pool size When the action is triggered Then access is denied and no changes are applied
Exception Recording Requires Reason Codes
Given a user performs an override that bypasses BayGuard holds (force‑assign into reserved, cancel hold, swap out flagged) When the override modal appears Then a reason code must be selected from the admin‑managed list and Save remains disabled until selected; an optional note up to 250 characters may be entered Given a valid reason code is selected When Save is clicked Then the override proceeds and the reason and note are attached to the affected order/bay records and audit log Given the modal is canceled When the user exits without saving Then no changes are applied
Audit Logging for Overrides
Given any override action is taken (reassign, pool resize, hold cancellation, ETA edit for flagged) When the action is committed Then an immutable audit entry is written with timestamp (UTC ISO‑8601), location ID, user ID, role, action type, affected order ID(s), bay ID(s), previous value(s), new value(s), reason code, note, and client device type, and it appears in the Audit tab within 5 seconds Given an auditor exports logs for a date range When Export CSV is requested Then a file is generated within 10 seconds containing all fields for the selected range Given an audit entry is viewed When a user attempts to edit or delete it Then the system prevents modification; only new correcting entries may be appended
Utilization, Compliance, and Impact Reporting
"As an owner, I want reports on reserved bay performance and compliance so that I can prove value, spot issues, and tune policies across locations."
Description

Track and report reserved bay utilization, hold adherence, swap counts, redirect effectiveness, wait times for flagged vs. non-flagged guests, and no-show rates. Provide dashboards and CSV export, location and time filters, and privacy-safe aggregation. Trigger alerts when compliance drops below thresholds or when guests are repeatedly redirected due to insufficient reserved capacity, enabling data-driven policy adjustments.

Acceptance Criteria
Utilization and Hold Adherence Dashboard Accuracy
Given a location with configured reserved bay hours and event logs for arrivals, bay assignments/holds, and releases within a selected date/time range When an operator opens the Utilization & Compliance dashboard for that location and range Then Reserved Bay Utilization Rate = total reserved-bay occupied minutes ÷ total reserved-bay available minutes is displayed within ±1 percentage point of a back-end recomputation for the same filters And Then Hold Adherence Rate = (flagged arrivals assigned to reserved bays on first assignment) ÷ (flagged arrivals in range) × 100% is displayed within ±1 percentage point And Then the dashboard shows a data refresh timestamp no older than 5 minutes from current time for streaming data
CSV Export: Metrics and Privacy-Safe Aggregation
Given a user with export permissions selects one or more locations and a date/time range and requests the BayGuard Reserve impact CSV When the export completes Then a downloadable CSV is available within 60 seconds for ranges ≤ 31 days and within 5 minutes for larger ranges And Then the CSV includes columns: date, location_id, location_name, reserved_bays_available_minutes, reserved_bays_occupied_minutes, utilization_rate, flagged_arrivals, hold_adherence_rate, bay_swaps, redirects_non_flagged_from_reserved, redirected_flagged_count, redirect_effectiveness_rate, median_wait_flagged, median_wait_non_flagged, p90_wait_flagged, p90_wait_non_flagged, no_show_rate And Then no direct identifiers (e.g., phone numbers, names, license plates) are present; any guest_id is an irreversible hash with per-day salt per privacy spec And Then totals in the CSV match dashboard totals for the same filters within ±1 unit or ±1 percentage point as applicable
Location and Time Filtering Behavior
Given a multi-location account with data across time zones When a user applies location filter(s) and selects a date/time range Then results include only events whose arrival timestamps fall within [start, end) interpreted in each location’s local time zone And Then multi-select aggregates metrics as sums and computes rates using appropriate denominators (e.g., occupied/available minutes) and presents a per-location breakdown toggle And When no records match the filters Then the UI shows an empty state and the CSV export returns headers with zero data rows
Compliance Threshold Alerting
Given a configured Hold Adherence threshold T% and evaluation window W minutes for a location When Hold Adherence Rate remains below T% for W consecutive minutes Then a "Compliance Drop" alert is sent via the configured channels (email/SMS/webhook) within 2 minutes of breach confirmation And Then alerts are de-duplicated so no more than one alert is sent per 30-minute period per location until recovery And When Hold Adherence Rate rises back to ≥ T% for at least 10 consecutive minutes Then a single "Recovered" notification is sent and the alert state is cleared
Reserved Capacity Insufficiency Alert
Given capacity alert thresholds are configured as either N flagged-guest redirects within W minutes or R% of flagged guests redirected in the last H hours When either threshold is met for a location Then an "Insufficient Reserved Capacity" alert is sent within 5 minutes including: counts, percentages, window parameters, and timestamps of the last 3 redirect events And Then the alert payload includes a suggested reserved-bay count adjustment derived from the prior 7 days’ peak concurrent flagged arrivals estimate And Then alerts are rate-limited to one per 60 minutes per location unless the suggested adjustment changes by ≥ 20%
Wait Time and No‑Show Differential Reporting
Given at least 30 arrivals per segment (flagged and non-flagged) exist in the selected period When the user opens the Wait & Impact panel Then the system displays median and p90 wait times for flagged vs. non-flagged guests, plus absolute and percentage differences, with values matching a back-end recomputation within ±1 unit or ±1 percentage point as applicable And Then No‑Show Rate is calculated as (arrivals that signaled "I'm here" but never occupied any bay and did not complete handoff within 15 minutes of scheduled window, excluding cancellations) ÷ total arrivals, and is displayed for each segment And When sample size for any segment is < 30 Then the UI labels that segment "insufficient data" and excludes it from differential calculations and CSV exports for that metric
Swap and Redirect Metrics Integrity
Given events produced by RightBay AI and BaySwap during the selected period When the Operations panel or CSV export is viewed Then counts for bay_swaps, redirects_non_flagged_from_reserved, and redirected_flagged_count represent distinct events and reconcile to arrivals within ±1 event for the filtered period And Then Redirect Effectiveness Rate = (redirected non-flagged guests who accept an alternative bay without staff contact) ÷ (redirected non-flagged guests) × 100% is displayed and exported with one decimal place precision And Then metric definitions are accessible via info tooltips and match the CSV column definitions

Assist Tap

Offers one‑tap assistance options at check‑in—trunk‑first, door‑side, mobility aid, carry‑to‑seat, or leave‑at‑passenger. Captains see precise prompts and runners get a concise checklist with one‑tap acknowledgments. Eliminates awkward back‑and‑forth and ensures the right help arrives the first time.

Requirements

One‑Tap Assistance Selector at Check‑In
"As a curbside customer, I want to indicate the type of assistance I need with one tap at check‑in so that staff arrive prepared without awkward clarifications."
Description

Embed a dedicated assistance module in the customer’s browser-based “I’m here” check‑in that presents tappable options (trunk‑first, door‑side, mobility aid, carry‑to‑seat, leave‑at‑passenger) with multi‑select and an optional short note. Persist selections in the order payload and surface a visual confirmation before submission. Ensure large tap targets, WCAG 2.1 AA accessibility, and fast load over low bandwidth. Associate assistance flags with the active order and bay assignment, maintain state on refresh, and gracefully degrade to standard handoff if no selection is made. No login or app required; compatible with common mobile browsers and SMS deep-link routing.

Acceptance Criteria
Customer Selects Multiple Assistance Options at Check‑In
Given I open the SMS deep link to the "I'm here" check‑in When the page loads Then I see tappable options labeled "Trunk‑first", "Door‑side", "Mobility aid", "Carry‑to‑seat", and "Leave‑at‑passenger" And each option is a selectable chip/button with a minimum 44x44 CSS px touch target and visible selected/unselected states And I can select any combination of options (0–5) and deselect by tapping again And an optional note field is available with a live character counter and a 140‑character limit And no login or app installation is required to interact with the module
Visual Confirmation Before Submission
Given I have selected one or more assistance options and/or entered a note When I tap Submit Then I am shown a confirmation summary listing my selected options and note And I can choose Edit to return to the form or Confirm to send And if no options are selected and no note is entered, the confirmation clearly states "No assistance requested" and proceeds to standard handoff upon Confirm And after Confirm, I see a success state indicating my assistance choices were received
Persist Assistance Selections in Order Payload and Associate With Bay
Given I Confirm my assistance selections When the order payload is created/updated Then the payload includes assistance.flags as an array of codes from {trunk_first, door_side, mobility_aid, carry_to_seat, leave_at_passenger} corresponding to my selections And assistance.note contains my note text (<=140 chars) or is null And the assistance object is stored with the active order ID and linked to the current bay assignment And staff views (captain and runner) display the assistance flags prominently on the active order with the assigned bay And the runner view renders a checklist with one‑tap acknowledgments for each selected flag
State Persistence on Refresh or Return
Given I have selected assistance options but not yet Confirmed When I refresh the page or reopen the SMS deep link within 30 minutes Then my selections and note are restored client‑side and remain editable And after successful submission, returning to the deep link shows a read‑only receipt of my assistance selections rather than a blank form And if the order is cancelled/expired, the assistance module is hidden and standard handoff messaging is shown
Performance on Low Bandwidth
Given a simulated 400 kbps bandwidth with ~400 ms RTT (Fast 3G) When the "I'm here" page loads with the assistance module enabled Then First Contentful Paint is <= 2.0 seconds And Time to Interactive is <= 3.0 seconds And total additional transfer size attributable to the assistance module is <= 150 KB gzipped across <= 2 HTTP requests And tap interactions (select/deselect) respond within 100 ms of input
Accessibility and Tap Target Compliance (WCAG 2.1 AA)
Given a user relies on assistive technologies (VoiceOver, TalkBack, switch/keyboard navigation) When navigating the assistance module Then all controls have programmatic labels, roles, and states announced correctly And focus order follows visual order and a visible focus indicator is present And color contrast for text and state indicators is >= 4.5:1 And each interactive target area is >= 44x44 CSS px with at least 8 px spacing and non‑overlapping hit areas And automated WCAG 2.1 AA audits report zero critical issues in the module
Cross‑Browser and SMS Deep‑Link Compatibility
Given an SMS deep link to the "I'm here" check‑in is tapped on a supported device When opened in iOS Safari 15+, iOS Chrome, Android Chrome 100+, Samsung Internet 19+, or Firefox Mobile 100+ Then the assistance module renders without layout breakage and all interactions function as specified And the deep‑link token persists across app switching/return without requiring login And if an unsupported browser is detected, the experience gracefully degrades to the standard handoff flow with a clear message
Staff Prompts and Runner Checklist Rendering
"As a runner, I want a concise, actionable checklist of the customer’s assistance needs so that I can deliver the order correctly the first time."
Description

Translate customer assistance selections into role‑specific UIs: the Captain view shows precise prompts on the order card, bay board, and alert feed, while the Runner view shows a concise, per‑item checklist with one‑tap acknowledgments and micro‑instructions (e.g., “open trunk before bag transfer”). Use clear icons, color badges, and ordering consistent with operational flow. Update in real time as orders move bays or are reassigned, and print succinct annotations on kitchen/runner tickets when enabled. Handle reconnection and device switching without losing state.

Acceptance Criteria
Captain Prompts Render Across Views
Given a customer selects one or more assistance options at check-in When the order appears in the Captain UI Then the order card, bay board, and alert feed each display the corresponding prompts with the correct icon and color badge for each selected option And prompt text is action-oriented and unambiguous for each option And prompts are ordered by operational flow (approach → vehicle prep → bag transfer → post-transfer) And no prompt block exceeds 3 lines on the order card And any change to assistance selections updates all three views within 2 seconds
Runner Checklist Generation and Acknowledgment
Given a customer selects assistance options at check-in When the order appears in the Runner view Then a concise per-item checklist is generated covering all selected assistance tasks And each checklist item has one-tap acknowledgment that timestamps completion and visually marks it done And micro-instructions (e.g., "open trunk before bag transfer" for trunk-first) are shown inline beneath the relevant task And checklist items are ordered according to operational flow And the order cannot be marked delivered until all checklist items are acknowledged
Real-Time Bay Move and Reassignment Sync
Given an order is assigned to Bay N and Runner A with some checklist progress recorded When the Captain moves the order to Bay M or reassigns it to Runner B Then the Captain and Runner UIs reflect the new bay and assignment within 2 seconds And the existing checklist state (completed/remaining items) is preserved And Runner B immediately sees the current checklist and prompts And Runner A no longer receives updates for the order And all changes are logged with timestamps
Ticket Printing of Assistance Annotations
Given ticket printing is enabled for kitchen and/or runner stations When an order with assistance selections is printed Then the ticket includes succinct assistance annotations using standardized abbreviations or ASCII icons And annotations include bay number (if assigned) and runner initials (if assigned) And annotations are limited to a maximum of 2 lines and 80 characters per line And disabling printing removes annotations from subsequent tickets at that station
Resilience to Offline, Reconnection, and Device Switching
Given a Runner or Captain has partially completed actions on an order When their device loses connectivity for up to 5 minutes and later reconnects or they sign in on a different device Then the latest prompts and checklist state are restored within 3 seconds of reconnection And previously recorded acknowledgments are neither lost nor duplicated And any offline acknowledgments queue locally and sync with original timestamps upon reconnect And if bay or runner assignment changed while offline, the updated assignment is shown immediately after restore
Accessibility and Visual Clarity for Prompts and Checklists
Given the Captain and Runner UIs render assistance prompts and checklists When tested in light and dark modes on standard device sizes Then icons have accessible labels/tooltips exposed to screen readers And color badges meet WCAG 2.1 AA contrast requirements And one-tap acknowledgment targets are at least 44x44 dp And micro-instruction text is at least 14pt, truncates with ellipsis when needed, and reveals full text on tap or expand
Real‑Time Acknowledgment and Customer Notifications
"As a customer, I want confirmation that my assistance request was seen and is on the way so that I know what to expect without calling."
Description

Allow runners to acknowledge each assistance item with a single tap and emit events to the order timeline for auditability. Notify customers via SMS when requests are seen and when a runner is en route, with templated, brandable messages that respect quiet hours. Start per‑option SLA timers at check‑in, trigger Captain alerts on impending breaches, and record resolution timestamps. Ensure idempotent updates, offline queueing for poor connectivity, and secure event signing for cross‑device consistency.

Acceptance Criteria
Single-Tap Acknowledgment Emits Timeline Event
Given an active curbside order with one or more assistance options selected at check-in When a runner taps Acknowledge on a specific assistance item Then that item’s status changes to Acknowledged within 1 second if online, or Queued if offline And an assist_acknowledged event including orderId, assistType, runnerId, acknowledgedAt (ISO-8601), deviceId, and eventId is appended to the order timeline exactly once And all signed-in devices viewing the order reflect the Acknowledged state within 3 seconds of server receipt
Customer SMS Notifications for Request Seen and Runner En Route
Given SMS notifications are enabled for the location and the order has a valid customer phone number and the current time is outside quiet hours When the first runner acknowledges any assistance item on the order Then send exactly one Request Seen SMS using template key Assist.RequestSeen, and record messageId and sentAt on the order timeline And when any runner marks En Route for the order Then send exactly one En Route SMS using template key Assist.EnRoute, and record messageId and sentAt on the order timeline And duplicate triggers within 5 minutes do not send additional messages
Brandable Templated Messages Render Correctly
Given SMS template keys Assist.RequestSeen and Assist.EnRoute are configured with variables brandName, orderCode, bayNumber, assistList, replyStopText When a message is rendered for an order Then no unresolved placeholders remain and brandName appears in the first 25 characters And the final GSM-7 encoded message length is <= 320 characters; if > 160 characters, multipart concatenation metadata (UDH) is set and parts count is recorded And the exact text sent is stored with template version and variables used
Quiet Hours Suppress Customer Notifications
Given the store’s quiet hours window is configured and the current local time is within that window When an event would trigger Assist.RequestSeen or Assist.EnRoute SMS Then no SMS is sent to the customer And the order timeline records a notification_suppressed event with reason QuietHours And no deferred or queued SMS is created
Per-Option SLA Timers and Captain Pre-Breach Alerts
Given assistance options are selected at check-in and SLA minutes per option are configured When check-in is saved Then start an independent SLA timer for each selected assistance option with startAt equal to check-in timestamp And when any timer reaches 80% of its SLA target Then send a Captain alert in the Captain Console naming the option and remaining time, and record a pre_breach event And if a timer reaches 100% without resolution Then send a breach alert and record a breach event with option, elapsed, and responsible team
Resolution Timestamps and SLA Closure
Given an assistance item is in Acknowledged or In Progress state When a runner marks the item Completed Then record resolvedAt (ISO-8601) on the item and compute elapsedSeconds from check-in And stop the item’s SLA timer and emit assist_resolved event with outcome Completed And update timeline and analytics so the item no longer appears in active queues within 2 seconds
Idempotent Updates, Offline Queueing, and Secure Event Signing
Given client operations include a deterministic idempotency key (eventId) per user action and events are HMAC-SHA256 signed with server-issued key When the same action is submitted multiple times within 10 minutes Then only the first valid request mutates state; subsequent duplicates return HTTP 200 Idempotent with no additional timeline event And if the device is offline when the action occurs Then the operation is queued locally with original timestamps and sent within 5 seconds of connectivity, preserving server-side ordering by sequenceNumber And the server rejects events with invalid signatures (HTTP 401) and those older than 15 minutes (HTTP 409 Stale), logging validation failures
Configurable Assistance Options and Location Defaults
"As an operator, I want to tailor assistance options and wording to my location so that the choices match our service and guests’ expectations."
Description

Provide an admin UI to enable/disable assistance options, reorder them, edit labels and helper text, add a custom option, and set per‑location defaults. Support localization and A/B variants, with versioned configuration that propagates to the check‑in UI and staff surfaces safely. Validate configurations to prevent conflicts, and fall back to a global default if a location setting is incomplete. Include permissioning, preview mode, and change logs for traceability.

Acceptance Criteria
Global Toggle and Ordering Propagation
Given a published global configuration where options X, Y, and Z are enabled and ordered [X, Y, Z] and a location has no overrides When a customer opens the check-in UI Then the assistance options appear in order [X, Y, Z] with only enabled options visible on customer, captain, and runner surfaces Given an admin disables option Y, reorders to [Z, X], and publishes When check-in UIs refresh at locations without overrides Then option Y is hidden and order updates to [Z, X] across all surfaces within 60 seconds of publish Given a location has an override enabling option Y When the global configuration disables option Y and is published Then that location continues to show option Y while locations without overrides do not
Editable Labels and Helper Text with Localization Fallback
Given an admin edits the default-locale label for 'trunk-first' to 'Pop trunk' and helper text to 'We will load your trunk first' and publishes When the check-in UI loads in the default locale Then the option displays label 'Pop trunk' and helper text on the customer UI, and captain/runner prompts reflect the updated text Given translations are provided for locales es and fr for the same option When a browser presents Accept-Language es-MX Then the es translation is used; if es is unavailable then fr is used; if neither is available the default locale text is used Given no translation exists for a provided locale When the admin previews that locale Then the UI indicates fallback to default and no missing-string placeholders are shown
Custom Assistance Option Creation and Staff Prompt Mapping
Given an admin creates a custom assistance option with label 'Carry to seat', helper text 'We will carry to your seated table', and staff prompt 'Carry order to seat' and publishes When a customer selects this option during check-in Then the captain view shows the prompt 'Carry order to seat' and the runner checklist contains a single acknowledgment item for this assistance, and the selection is stored with the order Given the custom option is later disabled and a new publish occurs When a new customer checks in Then the custom option is not presented, while historical orders retain the recorded selection in order history
Per-Location Defaults and Safe Fallback to Global
Given a location-specific configuration enables options [A, B] ordered [B, A] and leaves other options unset and is published When a customer checks in at that location Then only options [B, A] are shown in that order across customer and staff surfaces Given a location configuration is incomplete (e.g., contains zero enabled options) or invalid When a customer checks in at that location Then the system falls back to the latest published global configuration for that location and logs a configuration error with location id and version Given a location has a valid override When the global configuration changes Then the location continues using its override except for fields not specified in the override, which inherit from the global configuration
Versioning, Preview, Publish, and Rollback
Given an admin saves changes as Draft v2 When the admin opens Preview for Location L Then the customer and staff UIs for that preview session render v2 watermarked as 'Preview' without affecting live users Given Draft v2 is published When clients fetch configuration Then v2 becomes current and propagates to non-overridden locations within 60 seconds with no downtime; in-flight sessions continue with their current version until refresh Given an incident is reported on v2 When the admin rolls back to v1 and publishes Then v1 is reinstated as current within 60 seconds and the change log records a rollback event with previous version, new version, user, timestamp, and reason
A/B Variants Configuration and Consistent Assignment
Given the admin defines Variant A and Variant B with a 50/50 traffic allocation for Location L and publishes When 1000 eligible check-ins occur at Location L post-publish Then between 45% and 55% are served Variant A and the remainder Variant B, and each order consistently receives the same variant across customer and staff surfaces for the session Given the admin updates the allocation to 0/100 and publishes When subsequent check-ins occur Then all new check-ins receive Variant B within 60 seconds of publish Given an analytics event is emitted for a check-in When the event is recorded Then it includes variant id, configuration version, location id, and option selections
Validation, Permissions, and Change Logs
Given a configuration contains duplicate option keys or duplicate labels within the same locale When the admin attempts to save or publish Then the action is blocked and the UI lists each conflicting key/label and its locale Given A/B allocations do not sum to 100 or include negative values When publish is attempted Then publish is blocked with a specific validation error describing the invalid allocation Given a user without CONFIG_PUBLISH permission attempts to publish When the action is triggered Then a 403 error is returned, no configuration change is made, and an access-denied event is logged Given a user with CONFIG_EDIT and CONFIG_PUBLISH publishes a change When the publish completes Then the change log records timestamp, user id, role, scope (global or location), target location(s), version, and a diff of fields changed, and the log can be filtered by date range and location
SMS Keyword Fallback and Parsing
"As a customer who prefers texting, I want to request assistance by texting simple words so that I can get help without opening a link."
Description

Enable customers who don’t open the link to request assistance via SMS by parsing keywords and emojis (e.g., TRUNK, DOOR, ♿). Map parsed intents to the same assistance flags used by the web flow, support multiple keywords per message, and confirm via auto‑reply with an override link. Implement fuzzy matching, bilingual vocabularies, abuse/threat filtering, and confidence scoring. Reliably associate messages to the correct order via phone number and recent check‑in context, with opt‑out handling and full audit logs.

Acceptance Criteria
Parse SMS Keywords and Emojis to Assistance Flags
Given a live order linked to the sender’s phone number When the customer sends an SMS containing any recognized assistance keywords or emojis (e.g., TRUNK, DOOR, ♿) Then the system normalizes case/spacing/punctuation and maps them to the same assistance flags used by the web flow (trunk_first, door_side, mobility_aid) And multiple intents in a single message are all applied without duplicates And the parse result records a per-intent confidence score >= 0.80 for each applied flag
Fuzzy Matching and Bilingual Vocabulary Recognition
Given a live order linked to the sender’s phone number When the customer sends misspelled or variant terms (e.g., "trunq", "doorr") or Spanish equivalents (e.g., "maletero"/"cajuela" for trunk, "puerta" for door, "silla de ruedas" for mobility aid) Then the parser recognizes these via fuzzy matching and bilingual vocabularies and sets the correct flags And each accepted intent has confidence >= 0.80 And any token below 0.80 confidence does not set a flag and is included as unrecognized in parser output for the auto-reply
Auto-Reply Confirmation with Override Link and Language Matching
Given at least one intent is recognized for an inbound SMS When parsing completes Then send an SMS confirmation within 10 seconds summarizing the recognized assistance selections in the inferred language (EN or ES) And include a short override link that preloads the parsed flags in the web flow and allows changes And if no intent meets confidence >= 0.80, send a clarification SMS within 10 seconds (inferred language) asking the customer to tap the override link to choose options; do not set any flags
Order Association via Phone Number and Recent Check-In Context
Given an inbound SMS is received When the sender’s phone number matches exactly one active order created or checked-in within the last 120 minutes Then associate the message to that order and apply any parsed flags And when multiple active orders exist for the number, associate to the most recent unfulfilled order and record the disambiguation decision in the audit log And when no active order exists, send an SMS instructing the customer to tap their arrival link to start/restore context; do not set flags and log as unattached
Abuse/Threat Filtering and Opt-Out Compliance
Given an inbound SMS matches any CTIA opt-out keyword (STOP, STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT), case-insensitive Then immediately mark the number as opted-out, do not set assistance flags, and send a compliant confirmation within 10 seconds; log the opt-out And given the message is HELP or AYUDA, send a help response (support/how to rejoin) within 10 seconds; do not set flags And given the message triggers abuse/threat detection at or above severity threshold (>= 0.85), do not set flags; send a neutral acknowledgement and notify the captain channel; mask content from runner views; log moderation details
Audit Logging and Traceability for Parsed SMS
Given any inbound SMS (including opt-out, moderated, or unattached) When it is processed Then create an immutable audit record including: timestamp, raw and normalized text, phone (E.164), associated orderId (or null), recognized intents, per-intent confidences, inferred language, moderation/opt-out outcomes, auto-reply messageId(s), parser version, and actor for any override actions And audit records are queryable by orderId and phone and exportable as JSON; retention is at least 90 days
Robustness to Emojis, Attachments, and Noisy Messages
Given a message containing only the ♿ emoji When processed Then set mobility_aid=true with confidence >= 0.90 and confirm via SMS with override link And given an MMS with only media and no recognizable tokens, do not set flags; send clarification with override link within 10 seconds And given extra whitespace, punctuation, or mixed casing (e.g., " trunk, DOOR "), intents are still recognized correctly
Assistance Analytics and SLA Tracking Dashboard
"As an operator, I want analytics on assistance demand and fulfillment times so that I can staff appropriately and improve service."
Description

Add analytics that report assistance request mix by option, acknowledgment latency, time‑to‑handoff, dwell‑time impact, and SLA performance over time. Provide filters for location, shift, bay, and service type; trend charts; breach heatmaps; and CSV export. Anonymize PII, aggregate safely for low‑volume stores, and expose APIs for BI tools. Link chart points to underlying order timelines for root‑cause analysis and compare performance before/after configuration changes.

Acceptance Criteria
Assistance Mix by Option with Drill-Through to Order Timelines
Given a selected date range and filters (Location(s), Shift(s), Bay(s), Service Type(s)) When the user opens the Assistance Mix view Then the dashboard shows counts and percentages for each assistance option including "None" and the total equals the number of filtered orders And the trend chart displays a daily stacked percent series for the same options with zero-valued days shown explicitly And clicking any series point or legend entry opens a modal listing the underlying sanitized order timelines for that segment with columns [order_id_hash, option, bay, check_in, ack, handoff, departure, sla_met] And the modal paginates 50 rows per page up to 1,000 rows with server-side sorting by timestamp And all timestamps are localized to the location time zone and include offset And the sum of option counts matches the number of rows in the underlying order list for the same filters
Latency/Handoff/Dwell Metrics with SLA Evaluation and Pre/Post Configuration Compare
Given SLA targets for acknowledgment and handoff are configured at Org/Location level and a date range is selected When the user opens the Metrics view Then the dashboard displays p50 and p90 for acknowledgment latency (ack_ts - check_in_ts) and time-to-handoff (handoff_ts - ack_ts), plus SLA met rate computed per order as (ack_latency <= ack_SLA AND handoff_latency <= handoff_SLA) And dwell time is computed as (departure_ts - check_in_ts) and dwell-time impact is median(dwell | assistance=true) - median(dwell | assistance=false) within the selected filters And all durations are shown in whole seconds, with sample sizes displayed for each metric And when the Compare toggle is enabled and a configuration change is selected from the change log, the dashboard shows side-by-side metrics for equal-length windows immediately before and after the change effective time within the selected filters And the delta and percent change are shown; a significance badge appears only when both samples have n >= 30 and the absolute percent change >= the configured threshold And clicking any compared metric opens a drill-through to the corresponding pre or post order timelines
SLA Breach Heatmap Visualization
Given selected filters and a date range When the user opens the SLA Breach Heatmap Then a 7x24 grid (day-of-week x hour-of-day) renders where each cell shows breach rate (%) and volume for the period And cells with volume < suppression threshold T are labeled "Suppressed" and excluded from the color scale And the color legend is visible and reflects the min/max among visible (unsuppressed) cells And clicking a cell opens the list of breached orders for that cell, respecting all filters And initial heatmap render completes within 2,000 ms p95 for up to 100,000 orders in range
Filter Controls for Location, Shift, Bay, and Service Type
Given the user has access to multiple locations and the dashboard is loaded When the user applies any combination of multi-select Location, Shift, Bay, Service Type filters and a date range Then all widgets recompute using the intersection of selected filters And the applied filters persist in the URL and restore on reload or when the URL is shared And an Empty State is displayed when no results match And clearing filters restores defaults: last 7 complete days, All Locations, All Shifts, All Bays, All Service Types And p95 widget refresh time is <= 2,000 ms for up to 100,000 orders and <= 4,000 ms for up to 1,000,000 orders
CSV Export Fidelity and Scale
Given the user has applied filters and a date range When the user selects Export CSV -> Aggregated Metrics Then a CSV downloads within 60 seconds containing one row per day per assistance option with columns [date, option, orders, pct, ack_p50_s, ack_p90_s, handoff_p50_s, handoff_p90_s, sla_met_rate, dwell_impact_s] and values match on-screen aggregates And when the user selects Export CSV -> Order Timelines, the CSV contains one row per order with sanitized columns [order_id_hash, location_id, bay, service_type, option, check_in_iso, ack_iso, handoff_iso, departure_iso, ack_latency_s, handoff_latency_s, dwell_s, sla_met] with no PII And exports honor suppression rules; suppressed buckets are omitted from Aggregated Metrics and orders are excluded when inclusion would violate threshold T And timestamps are ISO 8601 with timezone offset; numeric fields use dot decimal; file is UTF-8 with a single header row
Privacy and Safe Aggregation for Low-Volume Stores
Given any analytics view or export When data is rendered or exported Then customer PII (name, phone, vehicle plate, free-text notes) is not displayed or exported; order identifiers are irreversibly hashed using SHA-256 with a per-tenant salt And a configurable minimum aggregation threshold T (default 5) is enforced; any bucket with count < T is suppressed in UI and exports And when a bucket is suppressed, aggregates roll up to the next coarser grain (hour->day, bay->all bays) where possible; if still < T, only overall totals are shown And audit logs record each export with user, timestamp, filters, row counts, and a hash of contents; logs and error messages contain no PII
BI API Parity and Access Controls
Given an OAuth2 access token with scope analytics.read and selected filters When a BI tool calls GET /v1/analytics/assistance with parameters (date_range, location_ids[], shift[], bay_ids[], service_type[], group_by=day|week|month) Then the API responds 200 with aggregates whose values match the UI for the same filters within 0.1% using identical metric definitions And the API supports cursor-based pagination and rate limiting (429 with Retry-After) and returns 403 for insufficient scope And GET /v1/analytics/assistance/timelines returns sanitized order-level timelines consistent with CSV export schema, with ISO 8601 timestamps and no PII And all responses include schemaVersion and generatedAt fields

BoldBoard Priority

Elevates accessibility arrivals on the live board with high‑contrast, enlarged tiles and clear priority badges. Sends optional haptic/chime alerts to staff devices and auto‑prompts the runner with the guest’s Assist Taps. Makes priority easy to see, act on, and verify without exposing extra PII.

Requirements

Accessibility Arrival Flagging via Arrival Link
"As a guest with mobility or sensory needs, I want to mark my arrival as accessibility priority and share my assistance preferences so that staff can find me quickly and accommodate me without repeated explanations."
Description

Add a single-tap Accessibility Priority option and configurable Assist Taps to the mobile web “I’m here” arrival flow. Store a non-PII priority flag and a set of standardized assistance preferences per order (e.g., trunk drop, passenger-side handoff), and pass them through to BoldBoard in real time. Ensure the arrival UI is fully accessible (WCAG 2.2 AA), localized, and performant on low-end devices. Update backend schema and events to support priority flag, assist_taps array, and audit metadata with strict data minimization and retention controls. Provide validation, rate limiting, and graceful fallback if preferences are not provided. Expose flags to downstream services (alerts, bay assignment, analytics) via existing pub/sub without introducing personally identifiable content.

Acceptance Criteria
Single‑Tap Accessibility Priority Selection
Given a customer opens the arrival link, When they tap the “Accessibility Priority” control once, Then the priority flag is set to true and the control shows a selected state with an accessible announcement “Priority selected”. Given the customer does not tap the control, When they submit arrival, Then priority is stored as false. Given the customer toggles the control on or off, When the form is submitted, Then the payload includes a boolean field priority set to the current state and contains no free‑text fields. Given the submission succeeds, Then the stored record for the order includes priority=true/false and no additional personal data is added by this action.
Assist Taps Multi‑Select of Standardized Preferences
Given the arrival page is loaded, When the customer opens Assist Taps, Then they can multi‑select up to 3 options from the allowlist ["trunk_drop","passenger_side_handoff","driver_side_handoff","no_contact"]. Given the customer submits the form, When assist_taps are provided, Then the payload includes assist_taps as an array of allowed values and no free‑text content. Given any provided assist_taps value is not in the allowlist, When the server processes the request, Then invalid values are ignored (not stored) and the request still returns 200 with the valid subset. Given no Assist Taps are selected, When the form is submitted, Then assist_taps is stored as an empty array [].
Real‑Time Flag Propagation via Pub/Sub (PII‑Safe)
Given an arrival with priority and assist_taps is accepted, When the backend publishes the event, Then a message is sent to the topic curbping.order.arrival within 200 ms service time containing only {order_id, arrival_id, priority, assist_taps, event_ts, source:"arrival_link", schema_version}. Given the message is published, Then BoldBoard receives and applies the update so that the tile shows a Priority badge and assist icons within 1.5 s p95 (3.0 s p99) end‑to‑end from client submit. Given multiple identical submissions for the same order within 60 s, When events are processed, Then downstream updates are idempotent (no duplicate tiles or alerts) based on arrival_id. Given the event schema is validated, Then it contains no disallowed fields (e.g., name, phone, email, plate, free‑text) and is rejected if any appear.
Arrival UI WCAG 2.2 AA Accessibility and Localization
Given the arrival page is audited, Then axe‑core reports 0 critical/serious violations and Lighthouse Accessibility score ≥ 95. Given keyboard‑only navigation, When the user tabs through controls, Then all interactive elements are reachable in a logical order with visible focus indicators and operable via Enter/Space. Given screen reader usage, When toggling Priority or selecting Assist Taps, Then control names, states, and changes are announced (e.g., “Priority selected/cleared”) and grouped with appropriate roles. Given user prefers‑reduced‑motion, When the page loads, Then motion/animations are reduced or removed. Given device locale is Spanish (es), When the page renders, Then all user‑facing strings are shown in Spanish with no clipped text at 320 px width; if a translation is missing, Then the string falls back to English. Given pseudolocalization is enabled in test, Then no hard‑coded strings are detected (all strings sourced from translation files).
Low‑End Device Performance and Resilience
Given a Moto G4‑class device on Fast 3G, When loading the arrival page cold, Then First Input Delay ≤ 100 ms p95 and Time to Interactive ≤ 3.0 s p75. Given the page loads, Then total compressed JS ≤ 120 KB and image assets ≤ 50 KB and no third‑party scripts block interactivity. Given the user taps controls, When updating Priority or Assist Taps, Then UI response occurs within 100 ms p95. Given the network drops after submit, When retries execute, Then the client retries up to 3 times with exponential backoff within 30 s and surfaces a clear status; if all fail, Then the user is shown a non‑blocking message to check in at the counter.
Backend Schema, Audit, Data Minimization and Retention
Given the database migration runs, Then the orders/arrivals record includes fields: priority_flag boolean default false; assist_taps text[] constrained to the allowlist; audit_metadata JSON with only {submitted_at, client_version, ua_family, ip_hash} and no raw IP or free‑text. Given an arrival is stored, Then no PII (name, phone, email, vehicle make/model/plate, free‑text) is persisted by this feature and the table enforces an allowlist of columns for writes. Given retention policies are configured, Then priority_flag and assist_taps are retained ≤ 30 days and audit_metadata ≤ 90 days, after which a scheduled job deletes or anonymizes them and produces a deletion log. Given an event is emitted, Then event payloads honor the same allowlist and TTL (messages include event_ts and schema_version for enforcement).
Validation, Rate Limiting, and Graceful Fallback
Given a submit request, When priority is not a boolean or assist_taps is not an array, Then the server responds 400 with a machine‑readable error code and no data is stored. Given a submit request with missing preferences, When processed, Then arrival succeeds with priority=false and assist_taps=[] and the board is updated accordingly. Given a client submits repeatedly, When more than 5 submissions occur for the same order_id or IP within 60 s, Then the server returns 429 with a Retry‑After header while preserving the first accepted arrival. Given duplicate submissions with the same idempotency key within 60 s, Then the server returns 200 and the same response without creating additional events.
BoldBoard Priority Tile UI
"As a curbside expediter, I want accessibility-priority arrivals to stand out on the live board so that I can identify and serve them immediately without scanning the whole queue."
Description

Visually elevate accessibility-priority arrivals on BoldBoard with high-contrast, enlarged tiles, position pinning to the top of the queue, and an unambiguous priority badge using color-blind-safe palette and iconography. Ensure tap targets and text meet or exceed WCAG 2.2 AA/AAA contrast and size guidance. Support responsive layouts for wall displays, tablets, and phones; maintain 60fps updates with virtualized rendering for large queues. Include a compact mode toggle that preserves badge prominence. Provide QA theming snapshots, accessibility checks, and a design token pack for consistent styling across clients. Do not reveal extra PII; show only order code, bay, ETA, and priority state.

Acceptance Criteria
Priority Tile Visual Elevation and WCAG Conformance
Given BoldBoard displays a mix of priority and non-priority arrivals When a priority tile is rendered in standard mode Then the priority tile height is at least 1.35x the non-priority tile height And the priority badge is visible within the tile bounds and not occluded And the badge text and icon have a contrast ratio of ≥7:1 against the badge background And all tile text (order code, bay, ETA) has a contrast ratio of ≥4.5:1 against its background (≥7:1 for the badge label) And any interactive tap target on the tile measures ≥44x44 CSS px And keyboard focus indicator around the tile has a contrast ratio of ≥3:1 and is at least 2px thick
Priority Pinning and Stable Sorting
Given a queue with N arrivals where some have priority=true and others priority=false When the board renders or receives an update Then all priority=true tiles appear before any priority=false tiles And priority tiles are sorted by arrivalTime ascending And non-priority tiles are sorted by arrivalTime ascending And reordering occurs within 250 ms of the update event And no tile visually oscillates position after it is placed (stable ordering)
Color-Blind-Safe Badge and Non-Color Redundancy
Given the board is viewed under deuteranopia, protanopia, and tritanopia simulations and in grayscale When a priority tile is inspected Then the tile presents a text label “Priority” plus a distinct icon or pattern independent of color And the badge silhouette/outline has a luminance contrast ratio of ≥4.5:1 against its immediate surroundings in all simulations And automated checks verify that color is not the sole indicator (at least two independent cues: text and icon/pattern are present)
Responsive Layouts Across Wall, Tablet, and Phone
Given the board is rendered at the following viewports: 1920x1080, 1366x768, 1024x768, 834x1112, 768x1024, 430x932, 390x844 When a priority tile is displayed Then no horizontal scrollbar is present And order code (up to 8 characters), bay, ETA, and priority badge are fully visible without overlap And the badge minimum height is ≥32 px on wall/tablet viewports and ≥24 px on phone viewports And the primary tile tap area remains ≥44x44 CSS px on touch devices And the grid auto-fits columns with a minimum column width of ≥280 px without clipping text
Virtualized Rendering and 60fps Updates at Scale
Given a test dataset of 500 active tiles (25% priority) and an update cadence of 1 Hz When running on reference devices (Chromebook i5-8250U/8GB and iPad 9th Gen Safari) Then the 95th percentile frame time is ≤16.6 ms and dropped frames ≤1% during continuous updates for 5 minutes And offscreen DOM nodes do not exceed 3x the number of visible tiles (viewport plus buffer) And total memory usage attributable to the board stays ≤200 MB And no main-thread long task exceeds 50 ms during the test
Compact Mode Toggle Preserves Priority Prominence
Given compact mode is toggled on via the UI control or URL parameter When the board re-renders Then the priority badge remains visually prominent with a minimum height of ≥24 px and text size ≥14 px And priority tiles remain at least 1.2x the height of non-priority tiles in compact mode And the toggle action applies within 100 ms without full page reload And the compact mode preference persists per device between sessions
Design Tokens, Theming Snapshots, Accessibility Checks, and Data Minimization
Given the design system repository When the BoldBoard Priority styles are delivered Then a versioned design token pack includes semantic tokens for colors, typography, spacing, border, and elevation for priority tiles And all priority tile styles consume tokens only (no hard-coded values) verified by lint rules And visual regression snapshots (light and dark themes) cover ≥6 viewports and show zero pixel diffs >1 px from the approved baselines And automated accessibility tests (axe-core/pa11y) report zero critical or serious violations on priority tiles And tiles display only order code, bay, ETA, and priority state; no names, phone numbers, or other PII appear in UI, ARIA labels, HTML attributes, or logs And network payloads consumed by BoldBoard for rendering exclude PII fields and are verified by automated contract tests
Multi‑Channel Priority Alerts to Staff Devices
"As a runner, I want an immediate but unobtrusive alert when an accessibility-priority guest arrives so that I can respond faster even when I’m away from the board."
Description

Send optional haptic and chime alerts to authenticated staff devices when an accessibility-priority arrival is detected or changes status. Implement web push notifications with vibration patterns on supported devices, plus in-app audio cues with per-user volume and do-not-disturb windows. Provide role-based subscriptions (runner, expediter, manager), rate limiting, and alert grouping to avoid noise during bursts. Messages must exclude PII and include only order code, bay assignment, and priority badge. Support kiosk mode with visual pulses. Include delivery/ack telemetry, reconnection logic for flaky networks, and per-location defaults configurable by managers.

Acceptance Criteria
Multi‑Channel Alert on Priority Arrival and Status Change
Given an authenticated staff device with alerts enabled and active session When an accessibility‑priority arrival is detected or the order status changes to Bay Assigned or Bay Reassigned Then within 2 seconds the device surfaces exactly one alert for the event And if Web Push with vibration is supported and permitted, a push notification is delivered using vibration pattern [200,100,400] And if Web Push is unsupported or permission denied, an in‑app haptic (if supported) and a chime are triggered And unauthenticated or unauthorized devices receive no alert
Per‑User Volume and Do‑Not‑Disturb Enforcement
Given a user sets chime volume to V between 0% and 100% When a priority alert fires Then the audio plays at volume V (0% = muted) Given a user defines a Do‑Not‑Disturb window from T1 to T2 (local time) When the device time is within [T1, T2) Then no audio or haptic is produced and a silent visual indicator is shown And the alert is logged as "silent" in the user’s inbox Given the user toggles DND or volume When the next alert occurs Then the new setting is applied without page reload and persists across sessions on that device
Role‑Based Subscriptions and Location Defaults
Given a user with role Runner, Expediter, or Manager at Location L When the user subscribes or unsubscribes to Accessibility Priority Alerts Then only subscribed users at Location L receive those alerts Given a Manager configures location‑level default subscriptions and presets (volume, DND) for each role at Location L When a new user of that role signs in at Location L Then the defaults are applied automatically Given a Manager updates the defaults Then non‑customized users reflect the change within 60 seconds And any user’s explicit override on their device takes precedence over location defaults
Rate Limiting and Alert Grouping During Bursts
Given N (>=2) priority events occur within a 5‑second window for the same location and device When alerts are emitted Then at most one chime/haptic occurs per 3 seconds per device And notifications are grouped under a single thread with summary "N priority arrivals" Given additional events arrive while a group is active Then the group count increments without exceeding the chime/haptic rate limit Given there is a 10‑second quiet period Then the next event starts a new group
PII‑Free Payload and Content Compliance
Given any priority alert payload is constructed Then it contains only: order_code, bay (number or "TBD"), priority_badge And it contains no customer name, phone, vehicle details, free‑text notes, or other PII Given server‑side validation runs before dispatch When a payload includes any prohibited field Then the message is not sent and a validation error is logged with correlation ID Given a client renders an alert Then automated checks confirm no PII is present in the UI
Kiosk Mode Visual Pulse Feedback
Given a device is in kiosk mode displaying the live board When a priority arrival event occurs Then the corresponding tile shows a high‑contrast pulse at 2 Hz for 3 seconds and repeats every 10 seconds until acknowledged or order completes And no audio is played in kiosk mode Given the kiosk loses connectivity or window focus Then a connectivity banner appears within 2 seconds And visual pulses resume within 2 seconds after reconnection/focus
Delivery/Ack Telemetry and Reconnection Resilience
Given an alert is dispatched Then a delivery_attempt is recorded with timestamp, hashed device_id, channel (push/in‑app), and outcome (delivered, failed, permission_denied) Given a user opens the notification or taps the in‑app alert Then an ack event is sent within 500 ms and linked to the delivery record And if offline, the ack queues and syncs within 5 seconds of reconnection Given the client loses network connectivity Then it retries the alert channel with exponential backoff up to 30 seconds And upon reconnection it replays missed priority alerts from the last 2 minutes without duplication
Assist Taps Prompt and Actionable Workflow
"As a runner, I want the guest’s assistance preferences to appear as actionable taps so that I don’t miss critical accommodations during a busy rush."
Description

When a staffer opens a priority tile, auto-present the guest’s Assist Taps as large, clear quick actions (e.g., Trunk Drop, Passenger-Side Handoff, Minimal Conversation). Allow one-tap acknowledgment per item, persist completion state to the order timeline, and surface a visible “All accommodations confirmed” checkmark. Provide offline-first caching and conflict resolution for multiple runners. Log structured analytics on tap exposure and completion. Ensure no free-text PII is displayed; only standardized labels and icons. Re-prompt unacknowledged items on handoff and before marking the order delivered.

Acceptance Criteria
Auto-Present Assist Taps on Opening Priority Tile
Given a priority order with one or more Assist Taps When a staffer opens the order’s priority tile on BoldBoard Then the Assist Taps render as large quick-action buttons within 500 ms of tile open And each button meets a minimum 44x44 dp touch target and WCAG AA contrast And each button shows only a standardized label and icon with no free-text PII And if there are ≤5 taps they are all visible without additional navigation; if >5, a clear scroll affordance and count indicator are shown
One-Tap Acknowledgment Marks Tap Complete
Given Assist Taps are displayed for an active order When a runner taps a quick-action once Then that tap’s state changes to Acknowledged within 200 ms with a visual state change and haptic/animation feedback And an acknowledgment event is recorded with order_id, tap_key, runner_id, device_id, and timestamp And the action is idempotent (re-tapping does not create duplicate events)
Persist Completion to Order Timeline
Given a tap is acknowledged by any runner When the order timeline is viewed on any device Then a timeline entry appears within 2 seconds of sync showing the standardized tap label, acknowledged status, actor (runner_id), and timestamp And the entry contains no free-text PII And the acknowledged state persists across page refreshes, device changes, and session reconnects
All Accommodations Confirmed Checkmark
Given all Assist Taps on an order are acknowledged When the order tile is displayed on BoldBoard Then a visible All accommodations confirmed checkmark appears on the tile and the order header And removing or adding taps updates the checkmark state within 1 second of sync And the checkmark never appears if any tap is unacknowledged
Offline-First Caching and Multi-Runner Conflict Resolution
Given a runner is offline and acknowledges one or more taps When the device reconnects to the network Then the acknowledgments sync within 5 seconds without user intervention And acknowledgments are monotonic (states can only move from Unacknowledged to Acknowledged) And conflicts from multiple runners are merged so that the earliest acknowledgment timestamp is retained and all actor_ids are captured in the audit trail And no acknowledgment is lost or reverted due to conflicts
Structured Analytics for Exposure and Completion
Given Assist Taps are displayed or acknowledged When events are logged Then an exposure event is emitted per tap on first render with order_id, tap_key, priority_flag, device_type, and timestamp And a completion event is emitted per tap on first acknowledgment with the above plus time_to_ack_ms And events contain no PII and use a deterministic deduplication key And batched analytics are delivered at-least-once with <1% duplication after dedupe
Re-Prompt on Handoff and Pre-Delivery
Given an order has one or more unacknowledged Assist Taps When the order is reassigned to a new runner or when a runner attempts to mark the order Delivered Then a re-prompt modal lists outstanding taps with quick actions And the runner must either acknowledge remaining taps or choose Unable to complete with a standardized reason before proceeding And the chosen path is logged to the timeline and analytics without PII
Priority Bay Assignment and Verification
"As a curbside lead, I want accessibility-priority arrivals assigned to suitable bays and verified at handoff so that we maintain accessibility standards and can track compliance."
Description

Auto-assign flagged arrivals to designated accessible bays using bay metadata and live geofence signal quality. Validate that the assigned bay is accessibility-compliant; warn and require explicit override if none are available. Display the assigned bay prominently on BoldBoard and in alerts. At handoff, require a quick confirmation that the accessibility accommodation and bay were honored, with optional standardized exception reasons. Feed compliance data to analytics and SLA dashboards. Support multi-tenant locations, manual reassignment, and real-time synchronization across devices without exposing PII.

Acceptance Criteria
Auto-Assign Accessibility-Flagged Arrival to Compliant Bay
Given an arrival is flagged for accessibility and bays have metadata including accessibility_compliant and signalQuality When the arrival is detected via the SMS “I’m here” link and geofence signal is available Then the system auto-assigns the arrival to an available bay where accessibility_compliant = true And the selected bay has the highest signalQuality among available compliant bays (tie-breaker: lowest bay number) And the assignment is completed within 2 seconds at p95 from arrival detection And only non-PII identifiers (e.g., order short code, bay number) are shown in UI and transmitted in assignment payloads
Override Flow When No Accessible Bays Available
Given all accessibility-compliant bays are occupied or marked out-of-service When an accessibility-flagged arrival requires a bay assignment Then the system blocks auto-assignment and displays a warning that no compliant bays are available And the user must provide an explicit override with a standardized reason or manager PIN to assign a non-compliant bay And the override action is logged with timestamp, userId, tenantId/locationId, selected bayId, and reason And the non-compliant assignment is labeled with an "Override" badge on BoldBoard and staff views
Prominent Bay Display and Alerts on BoldBoard
Given an accessibility-flagged arrival has an assigned bay When BoldBoard receives the assignment event Then the arrival tile renders with high-contrast theme, enlarged tile size, and a visible priority badge And the assigned bay number/label appears prominently on the tile and in staff alerts And subscribed staff devices receive a haptic/chime alert within 1 second at p95 if notifications are enabled And no additional PII beyond order short code and bay number is displayed
Handoff Verification of Accessibility Accommodation
Given a runner begins the handoff flow for an accessibility-flagged order When they attempt to complete the handoff Then the UI requires confirmation that the assigned accessibility bay and accommodation were honored And if not honored, a standardized exception reason must be selected before completion And the order cannot be marked complete without confirmation or a selected exception reason And the confirmation interaction completes within 8 seconds at p95 on supported devices And the confirmation/exception event is recorded with timestamp, userId, tenantId/locationId, bayId, and outcome
Compliance Telemetry and SLA Dashboard Updates
Given assignments, overrides, and handoffs occur for accessibility-flagged arrivals When each event is processed Then telemetry records eventType, timestamps, tenantId, locationId, arrivalId, assigned bayId, overrideFlag/reason, and handoff confirmation status And PII fields (e.g., phone number, name, vehicle plate) are excluded from telemetry And events are delivered to the analytics pipeline within 60 seconds at p99 with ≥99% success over a rolling 24h window And SLA dashboards update compliance rate, override rate, and time-to-assign within 2 minutes of event ingest And data is queryable by tenant/location without cross-tenant visibility
Manual Reassignment With Compliance Guardrails
Given an accessibility-flagged arrival has an existing bay assignment When staff manually reassigns the bay Then the system validates the target bay for accessibility compliance And if the target bay is non-compliant, an explicit override with a standardized reason is required And the reassignment propagates to BoldBoard and subscribed devices within 1 second at p95 And an audit record captures userId, fromBayId, toBayId, timestamp, tenantId/locationId, and override reason (if any)
Multi-Tenant Isolation and Real-Time Sync
Given multiple tenants operate at the same site with separate devices When any assignment, reassignment, or handoff update occurs Then only devices authorized for the originating tenant/location receive the event (0 cross-tenant deliveries in controlled test of ≥1000 events) And all authorized devices display a consistent state within 1 second at p95 using last-writer-wins with audit trail And all persisted and queried records include tenantId/locationId scoping validated by integration tests And no PII is exposed in inter-device sync messages
Privacy, Security, and Audit Controls for Priority Flows
"As an owner-operator, I want accessibility workflows that respect guest privacy and provide auditable records so that we protect customers while maintaining operational accountability."
Description

Enforce privacy-by-design for BoldBoard Priority: use non-identifying badges and standardized labels only; prohibit free-form notes in staff-facing UIs; avoid storing any additional PII beyond existing order identifiers. Implement role-based access for viewing priority indicators, field-level redaction in logs, encrypted transport, and configurable retention aligned with policy. Provide consent copy in the arrival flow explaining how accessibility preferences are used. Generate an audit trail of priority flag set/cleared events and Assist Tap acknowledgments for accountability and support. Include automated checks for WCAG and content security in CI.

Acceptance Criteria
Standardized Non‑Identifying Priority Badges
Given a staff user views BoldBoard Priority When a priority indicator is shown Then only standardized labels from the approved set {Access, Vision, Mobility, Hearing, Neurodiversity, Other} are displayed and no free‑form text is permitted And no customer names, phone numbers, emails, license plates, or arbitrary notes appear on tiles, alerts, or exports And priority badges render with high contrast and enlarged tiles without exposing PII And automated UI tests detect zero instances of unapproved strings or PII tokens across seeded sample data
No Free‑Form Notes in Staff UIs
Given any staff‑facing screen related to BoldBoard Priority When the screen is rendered Then there are no input controls that accept free‑form text about customers And attempts to submit unsupported fields via API are rejected with HTTP 400 and an error code NO_FREEFORM_NOTES And the database contains no columns or JSON paths for free‑form notes in priority entities
PII Minimization and Storage Constraints
Given any persistence of priority state Then only {orderId, locationId, priorityCode, assistTapSet, timestamps, actor userId/role} are stored And no additional PII (name, phone, email, vehicle plate, address) is created or stored beyond existing order identifiers When schema validation runs in CI and on migration Then tables and event schemas fail the build if PII fields are added or inferred And telemetry/events emit only non‑identifying IDs and codes
Role‑Based Access to Priority Indicators
Given an authenticated user without roles {Runner, FloorLead, Manager} When they access priority indicators via UI or API Then indicators are hidden or redacted and protected endpoints return 403 FORBIDDEN Given an authenticated user with roles {Runner, FloorLead, Manager} When they access priority indicators Then indicators are visible and associated actions are allowed And all access checks are enforced server‑side and logged with userId, role, decision, and reason (no PII values)
Field‑Level Redaction and Audit Trail
Given application logs and analytics sinks Then fields matching PII patterns (name, phone, email, plate) are redacted/omitted with a measured redaction rate of 100% in sampling tests When a priority flag is set or cleared Then an audit event is appended with {orderId, priorityCode, action, actor userId/role, timestamp, source} and contains no PII When Assist Taps are acknowledged Then an audit event is appended with the same non‑identifying fields And audit records are immutable (no update/delete API), filterable by date/orderId, and exportable as CSV/JSON And audit/log retention is configurable; given RETENTION_DAYS=30, records older than 30 days are purged by a daily job with a verifiable summary log
Consent Disclosure in Arrival Flow
Given a guest taps “I’m here” and is offered accessibility options When the options screen is shown Then consent copy explains how preferences are used solely to fulfill the order and links to the privacy policy And an explicit unchecked consent checkbox is required before attaching priority data to the order When consent is given Then a minimal record {orderId, consentGiven=true, consentVersion, timestamp} is stored; no new PII is added When consent is declined Then the order proceeds without priority flags and no accessibility preferences are stored
Secure Transport, CSP, and CI Automation
Given any app or API endpoint Then TLS 1.2+ is enforced with HSTS; HTTP requests are redirected to HTTPS; no mixed content is present in priority views And a restrictive Content‑Security‑Policy blocks inline scripts and unapproved origins; CSP reports contain no PII And CI pipelines run automated WCAG 2.1 AA checks for priority tiles/badges (contrast ≥4.5:1 for text, ≥3:1 for large text) and content‑security scanners Then the pipeline fails on any regression and publishes artifacts (a11y and CSP reports) retained for 90 days
Admin Configuration, Feature Flags, and Training Aids
"As a store manager, I want to configure and train my team on BoldBoard Priority so that it fits our operations and improves response times without confusion."
Description

Add admin settings to enable BoldBoard Priority per location, configure default alert behavior, choose badge copy within approved templates, and define accessible bay metadata. Ship a guided onboarding in BoldBoard with a one-minute walkthrough, tooltips, and a mock order sandbox to practice Assist Taps. Provide feature flags for staged rollout and A/B measurement of response times. Expose a lightweight dashboard for priority arrival SLAs, alert engagement, and accommodation completion rates. Include export-safe, non-PII metrics and in-app links to printable signage for accessible bays.

Acceptance Criteria
Enable BoldBoard Priority per location
Given I am a Location Admin and I open Admin > BoldBoard Priority settings for Location X When I set "Enable BoldBoard Priority" to ON and click Save Then priority tiles, badges, and Assist Tap prompts are activated for Location X within 60 seconds And compatible staff devices at Location X begin receiving alerts per the current default alert configuration And the setting persists across sessions and is scoped only to Location X And an audit record is created with admin user, location, old/new values, and timestamp When I set "Enable BoldBoard Priority" to OFF and click Save Then priority styling and alerts stop within 60 seconds for new events while existing non-priority operations remain unaffected
Configure default alert behavior
Given I am a Location Admin in Admin > Alerts When I configure Default Alert Type to Haptic, Chime, Both, or None and set Quiet Hours and Repeat-Alert throttle (e.g., 60 seconds) and click Save Then new priority arrivals at that location trigger alerts matching the defaults on compatible opted-in staff devices And Quiet Hours suppress sounds but retain on-screen indicators during the specified window And Repeat-Alert throttle prevents more than one alert per device per throttle window And a "Send Test Alert" delivers a test notification to my device within 5 seconds with a visible "Test" label And all changes are captured in audit logs
Badge copy templates without PII
Given I am a Location Admin in Admin > Badge Copy When I select a badge label from the approved template list (e.g., "Priority", "Accessibility", "Assistance") Then only approved, localized templates are selectable and freeform text input is not allowed And the selected label appears on newly created priority tiles within 10 seconds And the badge label and UI meet a minimum 4.5:1 contrast ratio And no customer PII (name, phone, vehicle plate) is displayed or derivable from the badge And changes are recorded in audit logs with user, selection, and timestamp
Accessible bay metadata and printable signage
Given I am a Location Admin in Admin > Bays When I define total bays, mark specific bays as Accessible, and assign optional notes (e.g., "van clearance") Then the BoldBoard shows accessible bay indicators and uses accessible bays as default targets when Assist Taps indicate accommodations And I can open "Printable Accessible Bay Signage" and download a PDF per bay that includes bay number, short URL/QR/SMS instructions, and accessibility icon only And the signage artifacts contain no customer-specific data or dynamic identifiers And validation prevents marking more accessible bays than total bays and requires at least one bay when enabling BoldBoard Priority And saved bay metadata takes effect within 60 seconds
Guided onboarding and sandbox practice
Given BoldBoard Priority is enabled at a location with no prior completion of the tour When a staff user opens BoldBoard Then a guided walkthrough is offered that can be started, skipped, or resumed, with total runtime not exceeding 60 seconds And contextual tooltips explain priority tiles, alerts, and Assist Taps And the user can launch a Mock Order Sandbox that simulates at least one priority arrival and Assist Tap flow without affecting live metrics or triggering real alerts And completion status is tracked per user and location and can be relaunched from Help And analytics record tour start/completion and sandbox usage
Feature flags and A/B measurement
Given I am a System or Location Admin in Admin > Experiments When I enable the BoldBoard Priority feature flag for a subset of locations or staff groups and start an A/B test with a 50/50 split (configurable) Then eligible orders/sessions are deterministically bucketed into control and variant cohorts And response time metrics (arrival-to-acknowledge, arrival-to-handoff) are captured per cohort with timestamps And the dashboard displays cohort counts, median, p90, and delta values for the selected date range And I can pause/stop the experiment and roll out to 100% with a single action And flags default to OFF for locations not explicitly targeted
Priority dashboard with export-safe metrics
Given I open Analytics > Priority Dashboard When I choose a location and date range Then I see priority arrival SLA attainment (using the configurable SLA threshold), alert delivery and acknowledgement rates, and accommodation completion rates And I can export a CSV that includes only aggregated metrics and non-PII fields (location ID, date, counts, medians/p90s, rates) And no names, phone numbers, free-text notes, or vehicle identifiers appear in the dashboard or export And exports complete within 10 seconds for 90th percentile of queries up to 31 days

FlexHold Window

Gives accessibility‑flagged orders a smarter prep and pickup grace window: kitchens hold at optimal temp closer to arrival, staging moves nearer the door, and runners get a ‘no‑rush’ cue. Protects food quality, avoids remakes, and respects guests who need a few extra minutes to park or transfer.

Requirements

Accessibility Order Flagging & Persistence
"As a shift manager, I want to flag an order (and optionally the guest profile) for accessibility so that our team adapts prep and pickup without extra steps each time."
Description

Enable staff to mark orders as FlexHold (Accessibility) at order creation or upon guest notification and optionally persist this preference to the guest’s phone number for future curbside pickups. The flag is visible across KDS/expo, runner, and host views and can be overridden per order. Includes data model changes, UI controls in order detail, and import mapping from POS/order feed. Provides audit logging of who set/cleared the flag, safeguards to prevent accidental application, and default behaviors (off by default, prompt on first use). Aligns with CurbPing’s SMS-first, no-app flow to ensure the flag travels end-to-end without additional hardware.

Acceptance Criteria
Flag at Order Creation (Order Detail UI)
Given a staff user opens an order detail and the FlexHold flag is off by default When the user selects “Mark FlexHold (Accessibility)” and confirms in the safeguard dialog Then the order is saved with FlexHold=true and the UI shows a persistent FlexHold indicator on that order And the change timestamp and user identity are recorded in the audit log And the flag change propagates to all connected views within 1 second
Flag from Guest SMS Notification
Given an active curbside order linked to a guest phone number When the guest replies with the supported accessibility keyword or taps the “I need extra time” link in SMS Then the system sets FlexHold=true on the matching open order for that phone number And the source is recorded as “guest SMS” in the audit log And staff-facing views receive a real-time toast/visual cue indicating FlexHold was enabled
Persist Preference by Phone Number (Opt-in Prompt)
Given a staff user sets FlexHold=true on an order for a phone number with no existing preference When the system prompts “Remember FlexHold for future curbside orders from this number?” and the user explicitly opts in Then the customer profile for that phone number is saved with FlexHoldPreference=true And future curbside orders from that number default to FlexHold=true And the opt-in event is recorded with user, timestamp, and order ID
Per-Order Override Against Persisted Preference
Given a phone number with FlexHoldPreference=true and a new curbside order is created When a staff user turns FlexHold off on that order and confirms in the safeguard dialog Then the order saves with FlexHold=false while the phone-level preference remains true And the override is recorded in the audit log with user, timestamp, and reason (if provided) And no other orders for that phone number are affected
Flag Visibility Across Operational Views (KDS/Expo/Runner/Host)
Given an order has FlexHold=true When any staff-facing view (KDS, expo, runner, host) displays that order Then the view shows a consistent, accessible FlexHold indicator (icon + text label) and a “no‑rush” cue for runners And the indicator is keyboard navigable and has 4.5:1 text contrast And any change to the flag is reflected across all views within 1 second
Import Mapping from POS/Order Feed
Given an inbound order payload includes a mapped accessibility tag/field per configuration When the order is ingested into CurbPing Then FlexHold is set according to the mapping without staff action And the mapping source and field values are stored in the audit log And if the payload lacks a mapped field, FlexHold remains off and a trace log records “no mapping applied”
Audit Logging and Safeguards Against Accidental Application
Given any action that sets or clears FlexHold When the action is taken via UI or system integration Then an audit entry is created with order ID, before/after value, actor (user ID or system), timestamp, source (UI/POS/SMS), and optional reason And UI toggles require an explicit confirm step (no default focus on confirm) and are undoable for 10 seconds And the system prevents bulk application without a second confirmation specifying count of affected orders
Adaptive Prep & FlexHold Scheduling
"As a kitchen lead, I want orders with accessibility needs to finish closer to actual arrival so that food quality is maximized without rushing the guest."
Description

Introduce logic that adjusts prep start, finish target, and hold grace windows for accessibility-flagged orders. The system aims to complete cooking closer to the guest’s arrival by using scheduled pickup time and the browser “I’m here” ping; if no arrival signal is received, apply an extended grace period before escalating. Show countdowns and cues on KDS/expo to delay bagging until optimal, and auto-switch back to standard timing for non-flagged orders. Works entirely within CurbPing’s existing SMS/link arrival detection (no hardware), with configurable buffers by store and daypart.

Acceptance Criteria
Adaptive finish targeting on 'I'm here' for accessibility-flagged orders
Given an accessibility-flagged order with configured finishLeadMinutes=2 and baggingLeadMinutes=1 When the guest taps the SMS 'I'm here' link before the order is marked Completed Then the system sets target finish time to now + 2 minutes and updates KDS/expo countdowns within 2 seconds Given an accessibility-flagged order that is already Completed when the 'I'm here' ping arrives When FlexHold is active Then the order remains in FlexHold and bagging is delayed until 1 minute before handoff, and a 'No rush' cue is shown on runner view within 2 seconds
Extended grace period without arrival ping
Given an accessibility-flagged order with scheduled pickup time T and configured holdGraceMinutes=10 When no 'I'm here' ping is received by time T Then the order enters FlexHold and is held up to T + 10 minutes with a visible countdown on KDS/expo Given the same order When the time reaches T + 10 minutes without a ping Then the system posts a single escalation banner on KDS/expo and marks the order Hold expired Given the same order after escalation When an 'I'm here' ping arrives Then the escalation clears and all countdowns retarget within 2 seconds
KDS/expo countdowns and bagging delay cues
Given an accessibility-flagged order When countdown to target finish is greater than baggingLeadMinutes Then KDS/expo shows 'Delay Bagging' state and disables 'Bagged' action for non-manager users Given the same order When countdown reaches baggingLeadMinutes Then KDS/expo enables 'Bagged' and highlights 'Prepare to bag' with a timer, and runner view shows 'No rush' until handoff Given a manager user When overriding is required Then the manager can force 'Bagged' at any time and the action is audit-logged with user, timestamp, and reason
Auto-switch to standard timing when not accessibility-flagged
Given a non-flagged order Then the system uses standard prepStartOffsetMinutes, finishLeadMinutes, and holdGraceMinutes, and no 'No rush' cues are displayed Given an order initially flagged When the accessibility flag is turned off before prep start Then the system recalculates using standard timings within 5 seconds and updates all countdowns Given an order initially flagged When the accessibility flag is turned off after prep start Then FlexHold-specific cues are removed immediately, and standard holdGraceMinutes apply upon completion
Store/daypart configurable buffers honored
Given store-level and daypart-level configuration for accessibility orders (prepStartOffsetMinutes, finishLeadMinutes, holdGraceMinutes, baggingLeadMinutes) When a flagged order is created during a configured daypart Then the system applies those values and records them on the order for audit Given an order in progress When a daypart boundary is crossed Then the originally applied configuration values remain in effect for that order and are not switched midstream Given a configuration change by an admin When saved Then new values apply only to orders created after the save, and a config version is stored with each order
SMS/link arrival detection only; dedup and matching
Given an accessibility-flagged order placed via a phone number When the guest taps the SMS 'I'm here' link Then the ping is matched to the order by phone/session and the order's ETA/finish targets update within 2 seconds without any hardware or app install Given multiple 'I'm here' taps from the same guest within 60 seconds When processing pings Then the system uses the latest timestamp and suppresses duplicate staff alerts within a 60-second debounce window
Food Quality Protection: Temp-Aware Holding and Remake Logic
"As an expediter, I want clear hold limits and prompts for accessibility-flagged orders so that I can keep items at peak quality without unnecessary remakes."
Description

Add menu-level hold profiles (max hold minutes, venting instructions, hot/cold station, and re-fire thresholds) that the system enforces more conservatively for FlexHold orders. Surface actionable prompts: when a hold profile is nearing its limit, cue a quick quality check, reheat, or re-fire suggestion instead of auto-remake. Print/label and KDS messages include holding guidance to keep items at optimal temperature. This reduces waste and remakes while protecting quality for guests who may take longer to reach the curb.

Acceptance Criteria
Menu Hold Profile Configuration and Enforcement
Given a menu item has a hold profile with maxHoldMinutes=12, station="Hot", ventingInstructions="Crack lid", reFireThresholdMinutes=10 When an order containing the item is marked Ready and the item is moved to Hold Then an item-level hold timer starts at 0:00 And the KDS tile displays "Max hold 12m • Re-fire at 10m • Vent: Crack lid • Station: Hot" And a "Hold until" timestamp equal to ReadyAt + 12 minutes is shown on KDS and printed labels
FlexHold Early Quality Cues and No Auto-Remake
Given an order is flagged FlexHold and contains an item with maxHoldMinutes=12 and reFireThresholdMinutes=10 When elapsed hold time reaches 70% of maxHold (8m24s) Then the KDS surfaces a "Quality check" prompt for that item And when elapsed hold time reaches the reFireThreshold (10m00s) Then the KDS suggests "Reheat" and "Re-fire" actions And the system does not auto-remake the item without explicit staff confirmation
Actionable Prompt Content and Outcomes
Given a nearing-limit prompt is shown for an item on KDS Then the prompt includes actions: "Quality Check", "Reheat (2m)", "Re-fire", "Snooze (3m)", "Dismiss" When "Quality Check" is selected Then a quality_check event with itemId, userId, and timestamp is recorded and the hold timer is unchanged When "Reheat (2m)" is selected Then the item status becomes "Reheating" for 2 minutes, the hold timer is paused during reheating, and resumes afterward at the previous elapsed time When "Re-fire" is selected Then a new production task is created for the item, the original item is marked "Re-fired", and a fresh hold timer starts at 0:00 when the remake is marked Ready When "Snooze (3m)" is selected Then the next prompt for this item is deferred by 3 minutes without exceeding the max hold limit
KDS and Label Holding Guidance Rendering
Given an item with a hold profile is routed to the KDS and label printer Then the KDS tile shows: Station, Venting instructions, Hold until time (site timezone), and any FlexHold badge And printed labels include: "Hold until HH:MM", Station, and Venting instructions in human-readable text And for FlexHold orders the KDS tile additionally shows a "No rush" cue for runners
Item-Level Timers for Mixed-Profile Orders
Given an order contains two items with different hold profiles When one item reaches its nearing-limit threshold Then prompts are shown only for that item, and the order tile indicates which item is at risk And reheat or re-fire actions on one item do not alter the hold timers of other items in the order
Exceeded Hold Limit Confirmation and Audit Logging
Given an item exceeds its max hold time Then a blocking confirmation appears with options: "Reheat", "Re-fire", "Serve as-is (require reason)" And selecting any option requires user identification and records an audit event with itemId, action, userId, timestamp, and pre/post elapsedHold values And the system never initiates an auto-remake without a recorded confirmation
Accessible Staging & Bay Assignment
"As a host, I want accessible orders automatically staged near the door and assigned to accessible bays so that handoff is simpler for the guest."
Description

Prefer accessible staging locations and the nearest-door bays for FlexHold orders. Allow managers to designate specific bays as accessible and define indoor staging spots near the exit. Auto-assign these resources when available, print the assignment on labels/tickets, and display it in runner/host views. Provide fallback logic when accessible bays are full and notify staff to offer assistance on arrival. Integrates with existing bay numbering and curbside workflows without requiring new hardware.

Acceptance Criteria
Configure Accessible Bays and Staging Spots
Given a manager with access to Settings > Curbside Configuration When they mark specific bay numbers as Accessible and designate named indoor staging spots near the exit as Accessible Then the configuration saves persistently and is visible on reload And the saved values are used by the assignment engine for subsequent orders And invalid bay IDs or duplicate staging spot names are rejected with a descriptive error and no changes are saved And an audit log entry records user, timestamp, and old/new values for each change
Auto-Assign Accessible Bay and Nearest-Door Staging for FlexHold
Given an order flagged Accessibility and FlexHold reaches the assignment step And at least one Accessible bay and one Accessible staging spot are unassigned When the assignment engine runs Then the system assigns the highest-ranked Accessible bay nearest the door and the nearest-door Accessible staging spot based on configured proximity ranking And reserves both resources for this order until pickup completion or cancellation And the assignment does not displace any active assignment on those resources
Fallback When Accessible Resources Are Full
Given an order flagged Accessibility and FlexHold And no Accessible bays or Accessible staging spots are available at assignment time When the assignment engine runs Then the system assigns the nearest available standard bay and standard staging spot And flags the order with a 'Offer Assistance on Arrival' staff cue And records a 'Accessible resources full' fallback event in the order timeline
Assignments Printed on Labels and Tickets
Given an accessibility-flagged order with assigned bay and staging spot When a kitchen label and runner ticket are printed Then both printouts include Bay=<number>, Staging=<name>, and Accessible=<Yes/No> And the values match the current assignment at print time And the fields are present on all supported printer templates
Runner and Host Views Show Assignment and Assistance Cue
Given an accessibility-flagged order is in the Active queue When viewed in Runner and Host screens Then the bay number, staging spot, and an 'Assist on Arrival' cue are displayed without additional clicks And if a fallback assignment was used, an 'Accessible bays full' indicator is shown And the display updates in real time if the assignment changes prior to arrival
Integration Without Hardware or Workflow Changes
Given a site using standard SMS-first arrival detection and existing bay numbering When the Accessible Staging & Bay Assignment feature is enabled Then no new hardware setup or pairing prompts appear anywhere in the workflow And non-accessibility orders continue to use the existing bay assignment logic with no change to bay numbering or arrival detection behavior And staff can complete curbside flows end-to-end without additional devices
Runner ‘No‑Rush’ Operational Cues
"As a curbside runner, I want a clear ‘no‑rush’ indicator on flagged orders so that I pace the handoff respectfully without fearing SLA penalties."
Description

Modify runner and host UIs to display a prominent “No‑rush” status for FlexHold orders with color and icon cues, and soften alert sounds to reduce urgency. Show a recommended patience buffer on the runner timeline, and prevent premature “late” warnings while still tracking SLA. Provide a one-tap toggle to request assistance if the guest asks, and keep the status consistent across all views until pickup is completed.

Acceptance Criteria
No‑Rush Badge Visibility in Runner and Host UIs
Given an order is flagged FlexHold When the order appears in Runner list, Runner detail, Host queue, and Kitchen screen Then a prominent "No‑rush" label with icon is displayed in all listed views And the label meets WCAG 2.1 AA contrast (≥4.5:1) against its background And the icon is ≥20px with tooltip text "No‑rush" And the badge uses the designated No‑Rush color token consistently across views And the badge persists until the order is marked Picked Up And non‑FlexHold orders never display the "No‑rush" label
Softened Alert Profile for FlexHold Orders
Given a FlexHold order triggers an arrival or staging alert When the UI plays an alert Then the alert uses the Soft profile at ≤50% of the standard alert volume (system‑normalized) And no escalation beeps, vibrations, or repeated tones occur during the patience buffer And at most one audible alert fires per FlexHold order per 2‑minute window unless assistance is requested And visual notifications still render as normal And non‑FlexHold orders use the standard alert profile
Patience Buffer Display on Runner Timeline
Given a FlexHold order is in Arrived status When the runner timeline renders Then a "Patience buffer" chip shows "+N min" where N = configured flexholdPatienceMinutes (default 5) And the chip counts down once per minute and never displays a negative value And the buffer is visualized as a shaded region before the Late threshold indicator And when N reaches 0, the chip hides automatically And tapping the chip reveals a tooltip: "No‑rush window; late warnings suppressed until it ends"
Suppress Premature Late Warnings While Tracking SLA
Given a FlexHold order is within its patience buffer When SLA timers would otherwise trigger a Late/Overdue warning Then no Late/Overdue labels, red highlights, or urgent badges are shown in any view And SLA timers continue running and are recorded in analytics with FlexHold=true metadata And when the patience buffer expires, Late warnings evaluate and display using the actual elapsed time And reporting preserves true elapsed and lateness values for the order
One‑Tap Assistance Request
Given a FlexHold order is Arrived When a Runner or Host taps "Request assistance" Then a help banner posts to Runner list, Host queue, and Manager channel with timestamp and order/bay number And the control toggles to "Cancel assistance" and can be cleared with one tap And the assistance state persists across devices and views until canceled or pickup is completed And only Runner, Host, and Manager roles can toggle the control; others cannot see or use it And an audit event is logged with orderId, userId, timestamp, and origin view
Cross‑View State Consistency
Given a change to a FlexHold order’s No‑rush, assistance, or buffer state on any device When the change is committed Then all active views (Runner list/detail, Host queue, Kitchen screen) reflect the change within 2 seconds And after a network reconnect, stale views reconcile to server state within 5 seconds And state updates are idempotent; duplicate events do not render duplicate banners or sounds And no two views display conflicting states for the same order after reconciliation
Customer SMS Reassurance & Assistance Flow
"As a guest with mobility needs, I want clear SMS messages that confirm I won’t be rushed and let me request help so that pickup feels stress-free."
Description

Adjust the SMS sequence for FlexHold orders to reassure guests they can take their time (e.g., “We’ll hold it warm—no rush”), include the bay/location details, and offer an ‘ASSIST’ keyword to request help. Route assistance requests to the host/runner view with a gentle alert, and allow staff to respond via two-way SMS templates. Support throttling, language templates, and store-level customization while keeping the flow app-free.

Acceptance Criteria
FlexHold reassurance SMS on check-in
Given an order is flagged FlexHold and the guest taps the 'I'm here' web link When the system records arrival Then send a reassurance SMS within 10 seconds that includes a 'no rush' reassurance phrase and the hold window end time And do not send a reassurance SMS for non‑FlexHold orders And send no more than one reassurance SMS per order unless a staff resend is explicitly triggered
Bay/location details included and updated via SMS
Given a FlexHold order with an assigned bay When the next SMS is sent to the guest Then the message includes the bay number and curbside signage/location descriptor tokens resolved (e.g., 'Bay 3 by the north door') And the message contains a short link to the bay map if configured Given a FlexHold order has no bay at check‑in When a bay becomes available Then send an update SMS with the assigned bay within 10 seconds And do not exceed throttling policies
'ASSIST' keyword help request routing to host/runner
Given an active pickup SMS thread for an order When the guest replies 'ASSIST' (case‑insensitive, trimmed) Then create an assistance ticket in the host/runner view with order ID, guest phone, order name (if present), bay (if assigned), and timestamp And display a gentle visual alert and single soft chime on host/runner devices within 5 seconds And auto‑reply to the guest within 10 seconds: 'We’re on our way to assist' (localized) And tag the conversation as 'ASSIST' in the staff UI
Two‑way SMS staff responses via templates
Given a conversation is open for an order (standard or ASSIST) When a staff member selects a predefined SMS template Then the message is sent within 5 seconds and appears in the thread for both staff and guest And delivery status (sent/delivered/failed where available) is recorded in the order timeline And staff can insert allowed tokens ({bay}, {eta_minutes}, {store_name}) before sending And any guest reply appears in the staff thread within 2 seconds of provider receipt
SMS throttling and rate limits for FlexHold flow
Given default throttling is enabled (per‑order minimum 30 seconds between automated messages; per‑store maximum 60 messages per minute) When the system attempts to send an automated SMS Then it enforces the per‑order spacing and per‑store cap, queuing excess messages FIFO up to 2 minutes And 'ASSIST' auto‑replies and staff‑initiated messages bypass per‑order spacing but count toward the per‑store cap And duplicate automated messages are suppressed within a 30‑second window
Language templates and store‑level overrides
Given language templates are configured at the store (at minimum English and Spanish) When the guest’s preferred language is known or a store default is set Then all automated SMS in the FlexHold flow use the selected language template And 'ASSIST' triggers assistance regardless of language; optional localized keywords (e.g., 'AYUDA') are supported if configured And stores may edit template wording but must retain required tokens ({assist_keyword}, {bay}, {store_name}); template validation blocks saves if tokens are missing
App‑free interaction guarantee
Given any FlexHold SMS message When a guest follows provided instructions Then all actions (check‑in, receiving bay details, requesting assistance, receiving staff replies) are completable via SMS and mobile web links only And no message contains links to an app store or prompts to install an app And all links resolve to mobile web pages that load within 3 seconds over 4G in 95% of tests
Privacy, Consent, and Data Retention Controls
"As a compliance-conscious operator, I want consent, role controls, and retention rules around accessibility flags so that we respect guest privacy while improving service."
Description

Implement explicit consent capture when persisting an accessibility preference to a phone number, with a one-time order-only option by default. Restrict visibility and edit rights to appropriate roles, mask the flag in exports, and allow opt-out and deletion. Apply retention limits and audit logs for changes to accessibility flags. Documentation and UI copy clarify purpose and usage, aligning with privacy best practices for sensitive preferences.

Acceptance Criteria
Persistent Consent Capture for Accessibility Preference
- Given a customer phone number placing an order, When the accessibility preference toggle is enabled, Then the default selection is one-time for this order only and the "Save to this phone number" option is unchecked. - Given the customer chooses "Save to this phone number", When they confirm, Then the UI displays purpose, retention summary, and a link to the Privacy Policy and requires an explicit affirmative action (unchecked-by-default checkbox or SMS reply "YES"). - Given persistent consent is granted, When the order is submitted, Then the system stores consent timestamp (UTC), channel (Web/SMS/Staff UI), actor ID when applicable, consent text version ID, and a phone hash, and associates the accessibility preference to the phone number. - Given persistent consent is not granted, When the order completes, Then the accessibility preference applies only to that order and is not persisted to the phone number.
Role-Based Visibility and Edit Controls for Accessibility Flags
- Given a user is not in Manager or Shift Lead roles, When viewing customer details or order history, Then the persistent accessibility flag is not displayed and only a per-order "no-rush" cue appears where relevant. - Given a Runner or Kitchen role, When attempting to edit a persistent accessibility flag via UI or API, Then UI controls are absent and API requests return 403 Forbidden with an audit entry. - Given a Manager or Shift Lead, When editing a persistent accessibility flag, Then the system requires a reason note and records who, when, before/after values in audit logs.
Masking Accessibility Flags in Exports and APIs
- Given any CSV export, When generated by any role, Then no column exposing the customer-level persistent accessibility flag is included. - Given order-level exports, When a 'no-rush' operational field is required, Then the export includes only a derived per-order 'no-rush' value without indicating persistence or customer-level status. - Given API requests for sensitive preference fields, When the caller lacks a service token with 'sensitive.read' scope, Then responses exclude the persistent accessibility flag; with scope, values are returned as masked booleans without customer-level linkage.
Opt-Out and Deletion via SMS and Staff UI
- Given a phone number with a saved accessibility preference, When the customer texts 'ACCESS OFF' to the CurbPing number, Then the system revokes consent, deletes the saved flag within 24 hours, and replies with a confirmation SMS. - Given a Manager in the Staff UI, When they click 'Delete saved accessibility preference' and confirm, Then the system deletes the flag immediately, shows success feedback, and writes an audit entry. - Given a deletion event completes, When the same phone places a new order, Then the default state is one-time only and no previous preference is auto-applied.
Retention and Automatic Purge
- Given a phone number with a persistent accessibility preference and no orders for 90 consecutive days, When the nightly retention job runs, Then the flag is purged within 7 days and an audit entry is recorded. - Given an order-level one-time accessibility selection, When 24 hours pass after order completion, Then any transient record is removed. - Given a phone number purged due to retention, When a new order is placed, Then no historical accessibility preference is populated.
Audit Logging for Consent and Flag Changes
- Given any create, update, or delete of a persistent accessibility flag or consent, When the action occurs, Then an immutable audit record is written with timestamp (UTC), actor (user ID or system), channel, action type, before/after values, reason (if provided), and source IP/user agent when applicable. - Given a Manager views audit logs, When filtering by phone hash and date range, Then results return within 2 seconds at the 95th percentile on a dataset of 1,000,000 records. - Given audit logs older than 12 months, When the archival policy runs monthly, Then logs are archived or purged per policy and the event is recorded.
User-Facing Copy and Disclosure for Sensitive Preference
- Given the consent UI is displayed, When a customer views it, Then copy includes purpose, optional nature, how to delete ("Text ACCESS OFF"), and retention summary in plain language with Flesch-Kincaid grade level ≤ 8 and a link to the Privacy Policy. - Given assistive technologies, When navigating the consent UI, Then all controls have accessible names, meet WCAG 2.1 AA contrast, and are operable via keyboard only. - Given Help Center documentation, When accessed, Then a page describing the accessibility preference, consent, opt-out, retention, and staff roles exists, was reviewed within the last 6 months, and is linked from the Staff UI.

Event Sync

Auto-pulls nearby game, concert, school, and weather calendars, then shapes pickup slot density before and after each event. Owners approve or tweak in one tap; Captains see a clear surge timeline on the arrivals board. Result: fewer manual edits, predictable peaks, and bays that keep moving without gridlock.

Requirements

Multi-Source Event Ingestion
"As an owner, I want CurbPing to automatically gather nearby event and weather data so that I don’t have to manually track schedules that affect pickup demand."
Description

Continuously fetches and normalizes nearby event and weather data into CurbPing from trusted sources (e.g., venue and city calendars, school ICS feeds, sports and concert APIs, and forecast/alert services). Applies per-location geofences, time windows, and time zone normalization; deduplicates by venue/time/title; enriches with venue geolocation, category, expected attendance, and reliability flags. Handles rate limiting, retries, and caching to ensure resilience, and outputs a standard Event object consumed by downstream prediction and scheduling services.

Acceptance Criteria
Per-Location Geofence and Time Window Filtering
Given a location configured with geofence radius R_meters and ingestion window [t0, t1] in that location’s timezone When the ingestion job runs Then only events with venue distance ≤ R_meters from the location centroid are ingested And only events with start timestamps within [t0, t1] are ingested And all stored event timestamps are normalized to UTC with originalTimezone captured
Multi-Source Deduplication and Merge
Given events from multiple sources that refer to the same real-world event (title similarity ≥ 0.90, start time difference ≤ 10 minutes, venue coordinates within 100 meters) When ingestion completes Then a single Event record is stored And Event.sourceIds contains all contributing source identifiers And duplicates are not published as separate Events And conflicts are resolved using precedence: official venue > league/API > ICS > other, with the chosen values persisted
Event Enrichment and Categorization
Given an ingested event with venue name/address but missing coordinates When enrichment runs Then venueLat and venueLng are populated via geocoding with accuracy radius ≤ 100 meters or the event is flagged reliability=low and quarantined And category ∈ {sports, concert, school, weather, other} is set using source metadata or deterministic rules And expectedAttendance is set from source when provided, else estimated from venue capacity and event type, else null And reliability is set to high for official sources, medium for league/API, low for others or unresolved geocodes
Resilience: Rate Limiting, Retries, and Caching
Given a source returns HTTP 429 or a transient 5xx When fetching that source Then the system retries up to 3 times with exponential backoff and jitter, honoring Retry-After when present And on repeated failure, a circuit breaker opens for 15 minutes for that source and the last successful cached response (age ≤ 30 minutes) is used And other sources continue ingestion unaffected And conditional requests (ETag/If-None-Match or If-Modified-Since) are used; HTTP 304 responses are treated as cache hits
Data Freshness and Update Propagation
Given a source updates an event’s time/details or cancels it When the next poll cycle occurs (interval ≤ 5 minutes) Then the corresponding Event is updated or marked canceled within 5 minutes of detection And Event.version increments and lastUpdatedUtc is set to the persistence time And downstream consumers receive the change notification/event within 1 minute of persistence
Event Object Schema Conformance
Given any event to be persisted or published When validating against the Event schema Then required fields exist: id, title, category, venueName, venueLat, venueLng, startTimeUtc, endTimeUtc, originalTimezone, reliability, status, sourceIds, lastUpdatedUtc And all times are ISO 8601 UTC; coordinates are WGS84 decimal degrees; reliability ∈ {high, medium, low}; status ∈ {scheduled, updated, canceled, archived} And any event missing id, title, venueLat/venueLng, or startTimeUtc is quarantined and not published downstream
Weather Forecast and Alert Ingestion
Given a weather forecast or alert overlapping the location geofence and within the ingestion time window When ingestion runs Then a Weather-category Event is created with severity mapped to {advisory, watch, warning} and start/end from the alert validity period And updates to the alert amend the existing Event within 5 minutes of detection And duplicates from multiple providers are deduplicated using area/time overlap ≥ 80% and title similarity ≥ 0.90
Local Impact Scoring
"As a store operator, I want the system to estimate which events will impact my pickup flow and when so that I can plan staffing and avoid congestion."
Description

Computes an impact score and surge windows for each ingested event using distance to store, venue capacity, category-specific pre/post-event curves, start/end times, historical order patterns, and forecasted weather severity. Produces a ranked set of surge intervals (with intensity and confidence) and filters out low-relevance events. Supports overlapping/b2b events, school dismissal waves, and storm fronts, yielding machine-readable surge timelines for capacity shaping.

Acceptance Criteria
Distance and Capacity Weighting Monotonicity
Given two events identical except distance (0.5 mi vs 5 mi) When Local Impact Score is computed Then impact(0.5 mi) > impact(5 mi) Given two events identical except venue capacity (2,000 vs 20,000) When Local Impact Score is computed Then impact(20,000) > impact(2,000) Given all other factors equal When distance decreases or capacity increases Then the computed impact score does not decrease
Category Curves Generate Pre/Post Surge Windows
Given a 'game' event with start=19:00 and end=21:30 at distance=1 mi When Local Impact Score computes surge windows Then at least one pre-event interval begins within 90–15 minutes before 19:00 and at least one post-event interval begins within 0–90 minutes after 21:30 Given a 'concert' event at the same venue When Local Impact Score computes surge windows Then total post-event intensity over [end, end+120m] > total pre-event intensity over [start-120m, start) Given a 'school-dismissal' event at 15:00 When Local Impact Score computes surge windows Then a surge interval starts within 10–30 minutes before 15:00 and lasts at least 20 minutes
Historical Pattern Modulation and Confidence Scaling
Given >5 past similar events (same category, DOW, and time band) with observed uplift U relative to baseline When scoring a new matching event Then predicted interval intensities reflect U directionally and confidence for those intervals is higher than for a similar event with no history Given no historical matches When scoring an event Then confidence values are lower than for a comparable event with >5 past instances and remain within [0,1] Given any produced surge interval When inspecting its fields Then confidence is present and in the range [0,1]
Weather Severity Influences Surge
Given a forecast storm spanning 15:00–21:00 with time-varying severity s(t) When Local Impact Score computes surge windows Then surge intervals fall within or adjacent to 15:00–21:00 and interval intensities are monotonically non-decreasing with s(t) Given two storms identical except overall severity (moderate vs high) When scoring Then the high-severity storm produces higher total predicted intensity over its window
Overlapping and Back-to-Back Events Blending
Given two events whose surge intervals overlap When Local Impact Score produces the timeline Then overlapping intervals are emitted as composite intervals where intensity = min(1.0, sum(component intensities)) and each interval lists contributing event_ids with their contributions Given two events with a gap < 15 minutes between end of the first and start of the second When the timeline is generated Then intervals are merged or emitted contiguously with no empty gap in the machine-readable timeline Given two non-overlapping events When the timeline is generated Then intervals remain separate and ordered by start time
Low-Relevance Event Filtering
Given a store-configured impact threshold T = 0.25 When Local Impact Score is computed Then any event with impact < T is excluded from the ranked list and contributes no surge intervals Given an event >10 miles away with capacity <500 and category 'other' When Local Impact Score is computed with T = 0.25 Then the event is filtered out Given an event with impact >= T When Local Impact Score is computed Then the event appears in the ranked list with its surge intervals
Output Schema, Ranking, and Timezone
Given Local Impact Score has completed When output is produced Then surge_intervals is a JSON array of objects each containing start_iso, end_iso, intensity (0–1), confidence (0–1), sources[{event_id, category, contribution}], tz, and timeline_version Given the produced surge_intervals When inspecting order Then intervals are sorted by start_iso ascending and non-overlapping except where composite intervals represent overlaps Given ranked events When inspecting order Then the list is sorted by impact descending with ties broken by earlier start_iso and each item includes event_id, category, start_iso, end_iso, impact (0–1), distance_km, capacity, confidence (0–1) Given any timestamp field When validating timezone Then it reflects the store's timezone tz and includes a UTC offset
Auto Slot Rebalancing
"As an owner, I want pickup slots to be automatically rebalanced around event peaks so that bays keep moving without gridlock and food stays hot."
Description

Automatically adjusts pickup slot capacity and bay allocation before and after predicted surges while respecting kitchen throughput, staffing constraints, prep-time buffers, SLAs, and store hours. Locks existing bookings, prevents overbooking, and proposes safe caps, lead-time shifts, and temporary bay conversions. Includes a simulation pass to validate that suggested changes avoid gridlock and maintain order freshness, and generates an applied-vs-proposed diff for user review.

Acceptance Criteria
Pre-Event Surge Auto-Rebalance Plan Generated
Given a detected local event within the configured radius and window And the store has operating hours overlapping the event window When the rebalancer runs at the configured lead offset or is manually triggered Then a proposed per-slot capacity and bay allocation schedule is generated for [event_time - pre_window, event_time + post_window] And each affected slot includes proposed_capacity, proposed_bays, and any lead_time_shift_minutes And the proposal is marked Pending Approval and time-stamped
Constraint-Adherent Capacity and Timing
Given configured constraints: kitchen_max_orders_per_interval, staffing_max_concurrent_orders, prep_time_buffer_minutes, SLA_max_wait_minutes, store_hours, and physical_bays When generating or applying a rebalance proposal Then per-slot capacity <= min(kitchen_max_orders_per_interval, staffing_max_concurrent_orders, physical_bays) And all slots remain within store_hours And for each order, prep_start_time >= open_time and ready_time <= close_time And implied_wait_minutes <= SLA_max_wait_minutes And hold_time_minutes <= freshness_max_hold_minutes
Locked Bookings Preserved and No Overbooking
Given existing bookings within affected slots When a rebalance proposal is generated or applied Then no existing booking pickup time, bay assignment, or order details are modified And proposed_capacity for any slot >= locked_bookings_count for that slot And the system blocks new bookings that would exceed proposed_capacity - locked_bookings_count And any slot where proposed_capacity < locked_bookings_count is auto-adjusted to locked_bookings_count and flagged in the diff
Lead-Time Shifts Applied to New Orders Only
Given a proposal with lead_time_shift_minutes for a defined window When a new order is placed within that window Then the quoted pickup time reflects lead_time + lead_time_shift_minutes And existing orders retain their original quoted pickup time and lead time And after window end, lead_time reverts to baseline automatically And the change is visible in checkout quotes and the kitchen queue ETA
Temporary Bay Conversions Scheduled and Reverted
Given convertible_bays are configured and physical_bays_total is known And predicted utilization in a window exceeds conversion_utilization_threshold When generating the proposal Then temporary conversions are included such that pickup_bays <= physical_bays_total and <= staffing_max_concurrent_orders And conversions have explicit start_time and end_time and do not overlap inconsistently And the arrivals board reflects bay count and numbering changes during the window And bays revert to default configuration automatically at end_time
Simulation Gate Keeps Against Gridlock and Stale Orders
Given a proposed plan and forecast arrivals distribution When the simulation runs for the affected horizon using configured service rates and variance Then max_queue_length <= available_bays for all intervals And average_wait_minutes <= SLA_max_wait_minutes And percent_orders_exceeding_freshness_hold <= freshness_breach_threshold And conflict_count (double-booked bays or overlapping prep allocations) == 0 And if any metric fails, the proposal is marked Needs Revision and is not applied
Applied-vs-Proposed Diff and One-Tap Approval
Given a pending proposal exists When an Owner or Captain opens the approval view Then the diff lists for each affected interval: previous_capacity, proposed_capacity, delta; previous_lead_time, proposed_lead_time, delta; previous_bays, proposed_bays, delta; impacted_orders_count; binding_constraint; simulation_summary And the user can Approve, Edit, or Reject in one tap And upon Approve, changes apply atomically, are audit-logged with user, timestamp, and proposal_id And upon Edit, the modified plan is re-simulated and must pass before approval is enabled And upon Apply, the arrivals board surge timeline updates within 30 seconds
One-Tap Owner Approval
"As an owner, I want to approve or tweak schedule changes in a single tap so that I retain control without spending time on manual edits."
Description

Mobile-first review screen that overlays proposed slot and bay adjustments on the daily schedule with a single approve action. Provides quick tweaks (± capacity sliders per window, snooze an event, disable a source for the day) and shows a succinct rationale (event name, timing, expected impact). Applies approved changes instantly with optimistic UI, and falls back to current settings if not approved by a configurable cutoff time.

Acceptance Criteria
Single-Tap Approval Applies Proposed Adjustments
Given I am an Owner on mobile viewing today's schedule with an Event Sync proposal overlay When I tap Approve Then the schedule updates within 1 second to reflect the proposed slot and bay counts using optimistic UI Given the approval request is sent When the server confirms within 10 seconds Then the optimistic state persists and the approval timestamp is stored Given the server rejects or times out When the client receives failure Then the UI reverts to prior settings within 2 seconds and displays a non-blocking error with a Retry action
Capacity Slider Tweaks Before Approval
Given I open the review screen for a specific event When I adjust the capacity slider for a time window within configured min/max bounds Then the proposed slot count and bay utilization for that window update in real time and show the net change Given I have adjusted one or more windows When I tap Approve Then the approved plan includes my slider tweaks and persists on refresh and across devices
Snooze an Event For Today
Given a proposed event is visible on the review screen When I tap Snooze for Today Then all proposals from that event are removed from today's schedule and will not reappear until the next local day Given I snoozed the event within the last 60 seconds When I tap Undo Then the event proposals are restored exactly as previously shown
Disable a Source For the Day
Given multiple proposal sources are enabled (e.g., Games, Concerts, Schools, Weather) When I disable one source for today Then no proposals from that source appear in today's review or affect the schedule, and the source auto-enables at 00:00 local time next day Given a source is disabled for today When I refresh or re-open the app Then the disabled state persists for the current day
Cutoff Time Fallback to Current Settings
Given a configurable approval cutoff time exists for each event window When the cutoff time is reached without Owner approval Then all proposals for that window are discarded and current schedule settings remain active with no partial application Given a fallback occurred due to cutoff When Captains view the arrivals board Then they see the baseline timeline without proposed surge indicators for the affected windows
Captain Surge Timeline Reflects Approved Plan
Given an Owner approval is successful When a Captain opens the arrivals board within 30 seconds Then the surge timeline displays the approved density changes and bay assignments for all affected windows Given an approved plan is modified (re-approval, undo, or new tweaks) When the changes are saved Then Captains see an in-UI update indicator and the timeline refreshes automatically within 10 seconds
Rationale and Audit Visibility
Given I am reviewing a proposal When I view the rationale Then I see event name, start/end time, source, and expected impact (delta slots/bays) in a concise row Given an approve, snooze, or disable-source action occurs When I open today's audit log Then the action, actor, timestamp, and delta summary are recorded and viewable
Surge Timeline Display
"As a Captain, I want a clear surge timeline on the arrivals board so that I can stage bays and direct customers efficiently."
Description

Enhances the Arrivals Board with a color-coded surge timeline showing upcoming peaks, bay utilization forecasts, and countdowns to event start/end. Displays event tooltips, confidence indicators, and recommended staggering guidance. Updates in real time as orders, approvals, and walk-ups change conditions, and is optimized for at-a-glance readability on shared tablets with accessible color contrast.

Acceptance Criteria
Surge Peaks Visualization
Given the Arrivals Board is open in landscape mode on a shared tablet And the venue has at least one assigned pickup bay configured When the timeline renders the next 180 minutes in 5-minute intervals Then each interval shows a color state based on forecasted bay utilization: green ≤60%, amber 61–85%, red >85% And intervals exceeding 85% are annotated with a peak icon and labeled with the time window And the timeline renders at least the next 120 minutes without horizontal scrolling And the color states persist after a screen refresh and match the same forecast data source
Forecast and Staggering Guidance
Given bay count and average handoff time are configured for the location And a forecast exists for the next 180 minutes When any interval’s predicted utilization exceeds configured bay capacity for that interval Then the interval displays a recommended cap per 5-minute slot (M) computed from bay capacity and handoff time And the guidance text includes “Cap to M/5 min” or “Stagger +N min” with N derived from overload magnitude And when utilization falls back below capacity, the guidance indicator disappears within 10 seconds And guidance values update immediately after owner approvals that alter slot density
Event Start/End Countdown Timers
Given at least one synced event starts or ends within the next 3 hours When the timeline is visible Then a countdown to event start is displayed until start time, then switches to a countdown to event end And countdown updates every minute when >10 minutes away and every second when ≤10 minutes And the countdown hides within 60 seconds after the event ends And multiple overlapping events display distinct countdown chips with event codes
Event Tooltip Details on Tap
Given the timeline shows one or more event markers When a user taps an event marker Then a tooltip opens within 200 ms showing: event name, type, venue, start/end (local time), source calendar, last sync timestamp, and surge window And the tooltip displays expected impact direction (Pre-event/Post-event) and any known attendance if available And tapping outside the tooltip dismisses it; only one tooltip is open at a time And if data is offline, the tooltip shows cached values with a “Stale” badge and last-updated time
Confidence Indicator and Legend
Given forecast confidence is computed for each event When the timeline renders event markers Then each event shows a confidence badge with bucketed levels: Low (0–49%), Medium (50–79%), High (80–100%) and the numeric percent And an info icon opens a legend explaining color scale, utilization thresholds, and confidence buckets And screen readers announce the confidence level and percent for each marker
Real-Time Updates from Orders, Approvals, and Walk-Ups
Given the Arrivals Board is connected to the network When a new order is scheduled, updated, or canceled affecting any future interval Then the affected intervals’ utilization and peak coloring update within 3 seconds And when an owner approves or tweaks an event’s slot shaping, the timeline reflects the change within 5 seconds And when a walk-up or “I’m here” arrival changes near-term load, the current and next two intervals update within 3 seconds And if connectivity is lost, a visible “Offline” badge appears and live updates pause until reconnected
Accessibility and Readability on Shared Tablets
Given the Arrivals Board is viewed on a 10-inch tablet at 1280×800 in landscape When the timeline and indicators render Then all essential text meets WCAG 2.1 AA contrast (≥4.5:1 for normal text, ≥3:1 for large text/icons) And tap targets for markers, chips, and info icons are at least 44×44 dp And primary timeline labels use a minimum font size equivalent to 14 pt and are not truncated And the interface supports a high-contrast mode and provides accessible names for all icons and badges
Proactive Surge Alerts
"As an owner, I want proactive alerts about upcoming surges so that I can review and approve adjustments before the rush hits."
Description

Sends timely SMS and in-app alerts when high-impact events or weather conditions are detected for a location, including an actionable summary and a deep link to the approval screen. Supports batching to reduce alert fatigue, quiet hours, per-location subscriptions, and escalation if no action is taken near the surge window.

Acceptance Criteria
SMS Alert on High-Impact Event Detection
Given a location has Proactive Surge Alerts enabled and a subscribed user list When an external event with impact >= configured threshold and within the configured radius is detected with a start time within the configured lead window Then an SMS alert is sent to all subscribed users for that location within 120 seconds of detection And the SMS includes: Location Name, Event Type, Event Name, Event Start Time (with timezone), Impact Category/Score, Proposed Slot Density summary (e.g., +20% 30m pre, +10% 60m post), and a deep link URL to the Approval screen And no duplicate SMS is sent to the same user for the same event within 60 minutes unless the event start time changes by >= 15 minutes or the impact score changes by >= 10 points And the delivery status (provider messageId, timestamp, outcome) is recorded in the Alert Log
In-App Alert and Deep Link Navigation
Given a user is authenticated and subscribed to alerts for a location When a high-impact event is detected for that location Then an in-app alert banner appears within 120 seconds And tapping the alert opens the Approval screen via deep link with locationId and eventId pre-selected And the Approval screen loads in <= 2 seconds on a typical 4G connection and displays the proposed slot-density adjustments And the alert is marked as read upon opening and the unread count updates immediately
Quiet Hours and Batching
Given a user has quiet hours configured (e.g., 22:00–07:00 local time) and multiple alerts are generated during that window When alerts occur during quiet hours Then real-time SMS are suppressed And a single digest SMS is sent at quiet-hours end or 30 minutes before the earliest event start (whichever is sooner) And the digest lists the number of alerts and, for each item: Event/Weather type, Name, Start Time, Impact, and a deep link to a batched approval view And no more than one digest is sent per 60-minute period during quiet hours
Per-Location and Per-Channel Subscriptions
Given a user has channel preferences set to in-app only for Location A and SMS + in-app for Location B When alerts are generated for both locations Then the user receives only in-app alerts for Location A and both SMS and in-app alerts for Location B And if the user unsubscribes from Location A Then no further alerts for Location A are delivered via any channel within 5 minutes of the change And subscription changes are audited with user, timestamp, and previous/new values
Escalation on Inaction Near Surge Window
Given an alert was issued and no approve/reject action has been taken When the event is T-60 minutes from start time Then an escalation alert is sent to the on-duty manager list for that location via all their subscribed channels, bypassing quiet hours And only one escalation per event per manager is sent And if an action is taken after escalation, no additional escalations occur And the escalation is logged with timestamp and recipients
Weather-Based Surge Alerts
Given the weather provider predicts heavy precipitation >= 0.5 in/hr or issues a Severe/Winter/Flash Flood Warning affecting the location within the next 6 hours When the condition is classified as high-impact per configured rules Then alerts are generated and delivered within 120 seconds including: Weather Type, Window/Start, Confidence/Severity, Proposed Slot Density summary, and deep link to approval And weather alerts follow the same batching, subscription, and escalation rules as event alerts And no duplicate weather alert is sent to the same user within 60 minutes unless severity category changes or start window shifts by >= 15 minutes
Delivery Failure Handling and Retry
Given an SMS delivery attempt returns a transient failure from the provider When the error is received Then the system retries up to 3 times with exponential backoff (approximately 1, 4, and 10 minutes) And each attempt and outcome is logged with provider codes And if all retries fail, a fallback in-app alert is posted to subscribed users and an admin-visible failure entry appears in the Alert Log And no duplicate SMS is sent if a late provider callback reports delivery after fallback
Schedule Audit & Rollback
"As an ops manager, I want an audit log and one-tap rollback of schedule changes so that I can troubleshoot and recover quickly from bad adjustments."
Description

Maintains an audit trail of proposed and applied schedule changes with source events, scoring inputs, and before/after slot capacities. Provides a one-tap rollback to a safe prior version, prevents reversions that would invalidate booked orders, and offers an exportable log for ops review and support troubleshooting.

Acceptance Criteria
Audit Entry on Proposed and Applied Changes
Given Event Sync generates a schedule change proposal When the proposal is created Then an audit record is created with state=proposed, a new monotonically increasing version number, and correlation_id And the record is visible in the location’s audit list within 1 second Given an Owner approves or edits a proposal When the change is applied Then an audit record with state=applied is created and linked to the proposal via correlation_id And applied_at timestamp and actor=owner are recorded
Recorded Fields: Source Events and Scoring Inputs
Given any audit record is created When the record is persisted Then it contains non-null fields: location_id, version, created_at (UTC), actor (system/owner), change_type (proposal/applied/rollback/blocked_rollback), source_event_id (if any), source_event_type (game/concert/school/weather/other), source_event_datetime, scoring_inputs (JSON), rationale (string, optional), and correlation_id And validation rejects saves missing any required field with an error logged
Before/After Slot Capacity Snapshot and Diff
Given a schedule change affects pickup slots When the audit record is saved Then it includes a per-slot array with slot_start, slot_end, capacity_before, capacity_after, and capacity_delta=capacity_after−capacity_before for all impacted slots And the sum of capacity_delta across impacted slots equals the total capacity change recorded at the header level And values are immutable after save
One-Tap Safe Rollback
Given a user views the audit timeline When the user taps Rollback on version N and confirms Then the system validates safety constraints and applies rollback within 3 seconds And schedule state matches exactly the slot capacities of version N And a new audit record is created with change_type=rollback, rollback_from_version=N, actor=owner, and a new version number And no booked orders are canceled, moved, or left unassigned
Rollback Guard Against Bookings Invalidation
Given a rollback target would reduce capacity below existing bookings or remove an assigned bay in any impacted slot When the user attempts rollback Then the rollback is blocked and no schedule changes are applied And a modal displays impacted slot count and minimum required capacity to proceed And the primary action to proceed is disabled until conflicts are resolved externally And an audit record is created with change_type=blocked_rollback including impacted_slot_ids and reason
Exportable Audit Log with Filters
Given a user selects a date range (up to 30 days) and location When the user exports the Audit Log Then a CSV is generated within 5 seconds for up to 10,000 records And the CSV includes columns: location_id, version, change_type, actor, created_at (UTC), local_time (location TZ), source_event_id, source_event_type, source_event_datetime, scoring_inputs, rationale, correlation_id, rollback_from_version, and total_capacity_delta And row count equals the number of records returned by the same filter in the UI

Elastic Slots

Dynamically expands or compresses 5‑minute pickup slots based on live kitchen throughput, runner capacity, and current dwell. Keeps quoted times honest, prevents overbooking, and protects food temperature. Staff feel the rush smooth out instead of spike, while guests see shorter, steadier waits.

Requirements

Real-time Capacity Signals
"As a shift lead, I want the system to use live kitchen, runner, and dwell signals so that slot sizing reflects our real capacity and avoids sudden backlogs."
Description

Continuously ingest and compute live operational signals used to size pickup slots, including kitchen throughput (orders in progress, prep-time percentiles), runner capacity (active runners and average handoff rate), and curbside dwell time (arrival-to-handoff). Normalize signals to a common time base, smooth short-term noise with rolling windows and hysteresis, and refresh calculations at sub-minute intervals. Provide fallbacks when a signal is missing (use last-known-good or merchant defaults) and expose a health state for each signal. Integrate with existing order lifecycle events and the browser “I’m here” arrival pings; ensure end-to-end latency from event to usable signal is under 10 seconds. All processing must be tenant-scoped, resilient to spikes, and continue operating during partial outages.

Acceptance Criteria
Sub-10s Event-to-Signal Latency
Given a new event (OrderAccepted, OrderFired, OrderReady, Handoff, or 'I'm here' arrival ping) arrives When the event is acknowledged by ingestion Then any dependent capacity signal is updated and available to downstream slot-sizing consumers within 10 seconds end-to-end And over 5 minutes at baseline load, the p99 event-to-signal latency is <= 10s and max <= 15s And under 5x expected peak sustained for 5 minutes, p95 <= 10s and p99 <= 15s, with 0 dropped events and <= 0.5% retries
Normalized and Smoothed Signals
Given raw inputs for kitchen throughput, runner capacity, and dwell have disparate timestamps and units When processed by the signal pipeline Then outputs are normalized to a common time base and emitted every 15 seconds aligned to wall-clock boundaries And with a configured 3-minute rolling window and 10% hysteresis on slot-impacting changes When inputs oscillate within ±9% for at least 2 minutes Then no change to the exposed signal value is emitted And when a true change exceeding 10% persists for >30 seconds Then the smoothed value steps to the new level within the next emit cycle and records the transition
Fallbacks for Missing Signals
Given any signal stops receiving fresh source data for >30 seconds When the next compute cycle runs Then the signal uses last-known-good for up to 5 minutes, marks health=Degraded, and tags value with fallback=true Given the outage persists >5 minutes When the next cycle runs Then the signal switches to merchant default value, health=Unavailable, fallback=true Given source data resumes When two consecutive fresh updates are processed Then the signal returns to live values within 10 seconds, health=Healthy, fallback=false
Per-Signal Health State Exposure
Given a tenant requests signal health When signals are operating Then the response provides, per signal, state in {Healthy, Degraded, Unavailable}, lastUpdateTs, ageSeconds, sourceStatus, and reasonCode And when a signal changes state Then a state-change metric/event is emitted and the health response reflects the new state within 5 seconds And all responses are tenant-scoped; cross-tenant requests are rejected
Integration With Order Events and Arrival Pings
Given order lifecycle events and 'I'm here' arrival pings are ingested When computing kitchen throughput Then derive active orders and prep-time p50/p90 from the last 15 minutes of OrderAccepted→Ready durations and emit every 15 seconds When computing runner capacity Then count active runners and compute handoff rate from the last 10 minutes of Handoff events and emit every 15 seconds When computing dwell time Then compute arrival-to-handoff using arrival pings and Handoff events, exclude records lacking either side, and emit percentiles every 15 seconds And if dwell sample size falls below 30 in the window Then use fallback per policy until the sample size recovers
Tenant-Scoped Processing and Isolation
Given events for tenants A and B are processed concurrently When signals and health are queried Then tenant A data never appears in tenant B responses and vice versa And given tenant A experiences a 10x burst for 60 seconds When observing tenant B Then B's signal update cadence and latency meet SLAs (p95 <= 10s), unaffected by A's burst And all processing, queues, and APIs require and validate tenantId; requests without tenantId are rejected
Resilience Under Spikes and Partial Outages
Given a 10x burst of mixed events for a tenant sustained for 60 seconds When monitoring the pipeline Then 0 dropped events, <= 1% retries, and event-to-signal latency meets p95 <= 10s and p99 <= 15s during the burst Given the order-event source is unavailable for 3 minutes while arrival pings continue When computing signals Then throughput health=Unavailable, runner capacity and dwell continue using available inputs and configured fallbacks, and updates continue every 15 seconds Given the source recovers When backlog replays Then events are applied in order without duplication and backlog drains within 2 minutes
Elastic Slot Sizing Engine
"As an operator, I want slots to automatically expand or compress based on capacity so that quoted times stay accurate and service flow remains smooth."
Description

Calculate dynamic slot widths and per-slot order capacity based on current capacity signals and forecasted short-term load. Start from a 5‑minute nominal slot and expand or compress the time window and/or allowable orders per slot within merchant-configured guardrails (e.g., 3–10 minutes and min/max orders). Apply smoothing and cooldowns to prevent oscillation, and never shrink an already promised window below the guest’s confirmation unless policy allows. Respect prep-time readiness constraints so food is ready near arrival, and publish a canonical schedule used by checkout, bay assignment, and notifications. Support multi-location time zones and handle preorders separately from ASAP demand.

Acceptance Criteria
Guardrail Enforcement for Slot Width and Capacity
Given merchant guardrails slot_length_min=3m, slot_length_max=10m, capacity_min=2, capacity_max=6 and nominal_slot=5m And current signals recommend slot_length=12m and per-slot capacity=8 When the engine computes the schedule Then the published slot length equals 10m And the per-slot capacity equals 6 And no slot length or capacity in the schedule exceeds or falls below the configured guardrails
Smoothing and Cooldown to Prevent Oscillation
Given smoothing_window=5m, cooldown_interval=3m, max_length_step=1m, max_capacity_step=1 And capacity signals fluctuate ±20% every minute for 10 minutes starting at t0 When the engine recomputes at 1-minute intervals Then slot length changes by no more than 1 minute per recomputation And per-slot capacity changes by no more than 1 order per recomputation And effective changes to slot length or capacity occur no more frequently than once per 3 minutes And no increase is immediately followed by a decrease (or vice versa) within the smoothing window
No-Shrink of Already Confirmed Guest Window
Given order O123 has a confirmed pickup window 17:10–17:20 local And policy.allow_shrink=false When subsequent recalculations would compress or shift the assigned slot narrower than 10 minutes or outside 17:10–17:20 Then O123’s promised window remains 17:10–17:20 across checkout, order detail, staff tablet, and notifications And internal capacity accounting continues to honor O123 within that original window
Prep-Time Alignment for ASAP Orders
Given an ASAP order is placed at 17:00:00 with predicted prep_time=12m and staging_time=3m And tolerance_ready_vs_arrival=±2m When the engine assigns a pickup window Then the midpoint of the assigned window occurs between 17:13 and 17:17 local And the predicted ready time deviates by ≤2 minutes from the assigned window midpoint
Canonical Schedule Publication and Consumer Consistency
Given a recomputation at t0 produces schedule version_id=V123 When checkout queries at t0+0.5s, bay assignment at t0+1.0s, and notifications at t0+1.5s Then all services receive the same schedule version_id=V123 And the schedule publish event is emitted within 1s of t0 And no service observes a mix of version_ids within a single request cycle
Multi-Location Time Zone and DST Correctness
Given locations A=America/Los_Angeles and B=America/New_York and all slot timestamps are stored in UTC When generating slots for A on 2025-03-09 01:30–03:30 local (spring-forward) and for B on 2025-11-02 00:30–02:30 local (fall-back) Then A produces no slots in the missing hour 02:00–02:59 local and displays correct local times for existing slots And B represents the repeated 01:00–01:59 hour without duplicating UTC timestamps and displays correct local times for both instances And cross-day boundaries reflect the correct local date
Separate Capacity Pools for Preorders vs ASAP
Given preorder P1 reserves 2 orders in the 18:30–18:40 window and ASAP demand exists for the same window And per-slot capacity (after dynamics) for 18:30–18:40 is 6 When the engine allocates capacity Then ASAP allocation cannot consume P1’s reserved 2 orders And total allocated (preorder + ASAP) does not exceed 6 And preorders scheduled >60 minutes in the future do not reduce ASAP capacity within the next 30 minutes
Checkout ETA & Slot Reservation
"As a guest, I want a reliable pickup window at checkout so that I can plan my arrival without unexpected delays."
Description

Integrate the elastic schedule into checkout to quote an honest pickup window and reserve capacity. Select the first suitable slot that meets readiness and capacity constraints, display the window to the guest, and hold the reservation for a short timeout to prevent race conditions. If conditions change before payment completes, requote once with clear messaging. After confirmation, place the order in the reserved slot and generate immutable guest-facing times within policy limits. Provide API/webhook surfaces for third-party ordering channels to retrieve available windows and confirm reservations consistently.

Acceptance Criteria
Earliest Suitable Slot Selection and Hold at Checkout
Given a cart with estimated readiness time and no existing hold When checkout requests a pickup window Then the system selects the earliest 5-minute window whose start time is >= readiness time and capacity is available per elastic schedule And displays the window in the shopper’s local timezone as "HH:MM–HH:MM" And creates a hold on that window for reserve_hold_ttl seconds (configurable 60–180) And decrements available capacity for the held window by the order’s capacity units for the duration of the hold
Hold Timeout Expiration and Release
Given a held window with an active hold timer When reserve_hold_ttl elapses without successful payment confirmation Then the hold is released and the window’s capacity is restored And the checkout shows "Pickup window hold expired" and prompts to requote And any payment attempt using the expired hold fails with error code HoldExpired (HTTP 409)
Single Auto-Requote Prior to Payment
Given a held window and the shopper has not completed payment When capacity or throughput changes invalidate the held window before payment capture Then the system performs at most one automatic requote in the session And selects the next earliest window meeting readiness and capacity constraints And the new window’s start time is not earlier than the original window’s start And the additional wait does not exceed the configured policy_max_additional_wait And the shopper is shown a clear message and must confirm the updated window before continuing And if the shopper declines or does not respond within 60 seconds, the hold is released
Post-Confirmation Slot Commitment and Guest ETA Immutability
Given a successful payment while a valid hold exists When the order is confirmed Then the order is committed to the held window and capacity is permanently consumed And the guest-facing pickup window becomes immutable for the customer And any attempt to change the pickup window via UI or API returns WindowImmutable unless allowed by post_confirmation_adjustment_policy that only widens the window without moving the start earlier
Concurrency Control and Idempotent Reservation Across Channels
Given multiple simultaneous reservation attempts for the same slot across tabs or channels When the API processes holds and confirmations concurrently Then no slot is allocated beyond its configured capacity And duplicate confirm requests with the same idempotency key return the original reservation without creating a new one And conflicting attempts receive a deterministic error (HTTP 409 CapacityConflict) without overbooking
Third-Party Availability and Reservation API with Webhooks
Given a third-party calls the availability endpoint with order metadata When requesting available pickup windows Then the API returns a list of windows with start, end, capacity_remaining, and a hold_token with expiry When the third-party confirms using a valid hold_token and idempotency key before token expiry Then the reservation is created and a webhook notification reservation.confirmed is delivered within 2 seconds including order and window details And confirms with expired or invalid tokens return HTTP 410 or 409 without creating a reservation
Policy Guardrails: Hours, Max Wait, and Window Size Limits
Given store hours, blackout periods, and max_quote_wait policy are configured When computing a pickup window for checkout Then the quoted window’s start and end are within store hours and do not overlap blackout periods And the quoted wait does not exceed max_quote_wait; otherwise "No slots available" is shown with the next available service time And the window size respects the elastic policy limits (min 5 minutes, max configured)
Bay-Constrained Scheduling
"As a runner lead, I want scheduling to respect our bay count so that arrivals match the space we actually have curbside."
Description

Synchronize elastic slot allocations with physical bay capacity to prevent curbside congestion. Use dwell forecasts to estimate concurrent vehicle count per slot and limit slot assignments so simultaneous arrivals do not exceed available bays. When demand exceeds bay capacity, automatically widen windows or defer to later slots per policy. Provide overflow behaviors (e.g., virtual queue with SMS instructions) and keep bay assignments consistent with the guest’s arrival signal to minimize misparked cars.

Acceptance Criteria
Enforce Bay Capacity in Slot Assignment
Given a configured bay count B and a dwell forecast that estimates per-order dwell intervals, When a new pickup requests slot T, Then the scheduler computes expected concurrent vehicles in T's overlap window, And confirms the booking only if projected concurrency <= B, And otherwise widens the slot or defers the order per policy until projected concurrency <= B or overflow is triggered.
Slot Widening and Deferral Policy
Given policy settings max_slot_width=20m, deferral_increment=5m, deferral_horizon=30m, When projected concurrency for the requested time exceeds bay capacity, Then the system widens the window in 5m increments up to 20m, And if still over capacity, it searches later slots in 5m steps up to 30m, And if no compliant slot is found, it routes the order to overflow.
Overflow Virtual Queue and SMS Instructions
Given no compliant slot exists within policy, When the order is enqueued to overflow, Then the guest receives an SMS within 5 seconds containing queue position, estimated wait, and holding instructions, And the staff dashboard displays the overflow entry with ETA and bay-to-be-assigned, And when a bay becomes available, the guest receives an SMS with the bay number and the order auto-assigns to that bay.
Stable Bay Assignment on Arrival Signal
Given a guest clicks the 'I'm here' link within their quoted window, When a bay is assigned, Then that bay remains fixed for the guest through their dwell, And any necessary reassignment occurs only if no other free bay exists and is accompanied by an immediate SMS update and dashboard update within 2 seconds.
Forecast-Driven Concurrency Blocking
Given bays=2 and existing orders A and B with forecast dwell overlapping 12:00–12:10, When order C requests a 12:05 pickup, Then the scheduler denies 12:05 unless widening/deferral produces a time where at most 2 orders overlap, And the audit log records the decision path (widened, deferred, or overflow) with timestamps.
Staff UI Capacity Guards and Overrides
Given the scheduling board shows upcoming slots, When a slot reaches projected concurrency equal to bay capacity, Then the slot is marked Full and disabled for selection, And only users with Force Override permission can assign into Full slots, requiring a reason note, And all overrides are logged and visible in the activity feed.
Real-Time Concurrency Recalculation for Early/Late/No-Show
Given arrivals may be early, late, or no-show per configured grace period, When a guest signals arrival early/late or is auto-marked no-show after grace expires, Then the system recalculates projected concurrency and bay availability within 2 seconds, And releases or reassigns bays accordingly, And any affected guests receive updated quoted times or bay instructions via SMS.
Staff Load Console
"As a floor manager, I want a clear view of current and upcoming load so that I can make quick staffing and pacing adjustments."
Description

Present live and near-term (next 60 minutes) visibility of slot compression/expansion, forecasted arrivals, and capacity utilization. Clearly indicate the current slot window, order count per slot, and any applied guardrails or cooldowns. Provide safe, time-bound overrides (e.g., temporarily cap orders per slot) with audit logging. Surface alerts when the system widens windows beyond a threshold, when bays risk overfill, or when signals degrade, so staff can proactively adjust staffing or pacing.

Acceptance Criteria
Live Slot Timeline Visibility
Given I am a staff user on the Staff Load Console When the console loads Then the current slot window is highlighted and labeled with start and end times in 5-minute increments Given the console is loaded When viewing the timeline Then the next 60 minutes are segmented into 12 five-minute slots showing each slot’s compression/expansion factor (e.g., 0.5x–2.0x) Given forecasted arrivals are computed When viewing the timeline Then each slot displays the forecasted arrival count with a prediction interval Given throughput or dwell signals change When new data is received Then the timeline updates within 5 seconds and reflects slot expansion/compression deltas
Per-Slot Order Counts and Capacity Utilization
Given the Staff Load Console is open When viewing any slot Then I see the scheduled order count and the dynamic capacity utilization percentage for that slot Given orders are created, moved, or canceled When the slot content changes Then the order count and utilization recalculate and display within 5 seconds Given a slot’s utilization crosses 90% When the threshold is exceeded Then the slot is visually flagged with a warning state
Guardrails and Cooldowns Indicators
Given any global or per-slot guardrail (e.g., max widen, min narrow) or cooldown is active When viewing the timeline Then an indicator shows the active rule type, scope (global or slot), and remaining duration via tooltip or details panel Given a guardrail or cooldown expires or is removed When its status changes Then the indicator is removed within 5 seconds and slot calculations refresh
Time-Bound Override: Cap Orders Per Slot
Given I have Manager role When I open override controls for a future slot within the next 60 minutes Then I can set a temporary order cap as an integer between 1 and the system-calculated maximum and select a duration between 5 and 60 minutes Given an override is saved When confirmation appears Then affected slot(s) immediately display an override badge, updated capacity, and a countdown of remaining override time Given an override is active When incoming orders would exceed the temporary cap Then new orders are scheduled into the next available slot according to Elastic Slots rules
Override Expiry and Auto-Reversion
Given an override has a defined duration When the duration elapses Then the affected slot(s) revert to system-calculated capacity within 5 seconds and the override badge disappears Given auto-reversion occurs When the reversion completes Then a confirmation notice is shown with timestamp and reference ID Given a manager cancels an override early When cancel is confirmed Then the same reversion behavior occurs immediately
Audit Logging for Overrides and Critical Actions
Given a manager creates, updates, or cancels an override When the action completes Then an audit record is stored with user ID, role, timestamp (UTC), affected slot(s), previous and new values, duration, and reason (free-text up to 140 characters) Given audit records exist When I open the audit log view and filter by date/time range or user Then matching records are listed with immutable details and can be exported to CSV with ISO-8601 timestamps
Alerts: Widening Threshold, Bay Overfill Risk, Signal Degradation
Given the system widens any slot beyond the configured threshold (e.g., >1.5x base window) When the change is applied Then an in-console alert banner appears within 5 seconds highlighting the affected time window Given forecasted bay utilization exceeds 100% for any 10-minute horizon within the next 30 minutes When the forecast is computed Then a warning alert lists the at-risk window, estimated overflow, and a recommended action (e.g., add runner or apply override) Given arrival detection signal quality drops below configured thresholds (e.g., ping success rate <80% over 10 minutes or location uncertainty >100m) When the degradation is detected Then a caution alert is raised showing the breached metric and a link to diagnostics
Guest SMS Window Updates
"As a guest, I want timely SMS updates about my pickup window so that I’m not surprised if timing changes."
Description

Send SMS confirmations that include the assigned pickup window and bay guidance, and issue updates if the window shifts beyond a merchant-defined threshold. Throttle updates to avoid spam, require explicit consent where needed, and include one-tap links for “Running Late” and “I’m Here” that feed dwell and arrival signals. Ensure local timezone correctness and failover to static instructions if dynamic scheduling is temporarily unavailable.

Acceptance Criteria
Initial SMS Confirmation With Window and Bay Guidance
Given a new curbside order is accepted at a location with Elastic Slots active And the customer phone number is valid and SMS-capable or has web-SMS fallback When the initial pickup window is first computed Then send a confirmation SMS within 10 seconds that includes: pickup window start–end (e.g., "3:40–3:55 PM"), timezone abbreviation, bay guidance (specific bay if preassigned or instruction to tap "I'm Here" for assignment), store name, short pickup address, and the order short ID And include one-tap links for "I'm Here" and "Running Late" using a signed, order-scoped token And record the message ID, send timestamp, and payload hash in an audit log And if explicit consent is required and not present, route to the consent flow instead of sending this confirmation
Threshold-Based Window Shift Update
Given an order with a previously communicated pickup window and a merchant-defined update threshold T minutes When the recomputed window start or end changes by at least T minutes Then send an update SMS within 15 seconds that states the new window and that the window was updated And include the same one-tap links with the same order token And do not send an update when the absolute shift is less than T minutes And do not send an update if the order is already completed, canceled, or picked up And persist the prior and new window values with timestamps in the audit log
Update Throttling and Consolidation
Given multiple window recalculations occur for the same order When shifts that meet threshold occur within a consolidation window C minutes (default 2 minutes) Then consolidate them into a single update SMS reflecting the latest window And enforce rate limits of at most 1 window update per 5 minutes per order and at most 3 window updates total per order (initial confirmation excluded) And suppress any over-cap messages while logging the suppression reason And expose the effective caps and last-send timestamp to the UI/API for observability
One-Tap Arrival and Running Late Links Feed Signals
Given a customer taps the "I'm Here" link with a valid signed token When the arrival endpoint is invoked Then record an arrival event with timestamp and phone fingerprint, assign or confirm a bay, and update staff UI within 2 seconds And return a mobile web response confirming the bay and curbside instructions Given a customer taps the "Running Late" link with a valid signed token When the lateness endpoint is invoked Then extend the expected arrival by a merchant-configured amount (default 10 minutes), update dwell forecasts, and recalculate Elastic Slots And ensure both links are idempotent: repeated taps update the latest event without duplicates and return current state And reject invalid/expired tokens with a safe retry path (request new link) without logging PII
Explicit Consent Capture and Compliance
Given the pickup location is in a jurisdiction requiring explicit SMS consent and the customer has not opted in When the order is created Then send a single consent-request SMS including brand name, purpose (transactional curbside updates), expected frequency, STOP/HELP keywords, and a link to terms/privacy And only send confirmation/updates after recording an affirmative consent event with timestamp, jurisdiction, and message ID And immediately honor STOP by ceasing further messages and recording suppression And respond to HELP within 10 seconds with contact info and instructions And persist consent status per phone number and merchant with auditability
Local Timezone Correctness
Given the pickup location’s canonical timezone and current DST rules When formatting pickup windows for SMS Then display times in the pickup location timezone with an explicit abbreviation (e.g., PT) And include the service date when the window crosses midnight or falls on a DST transition day And store all window times in UTC with the source timezone offset for lossless conversion And verify formatting with unit tests covering standard time, daylight time, and DST transitions
Failover to Static Instructions on Dynamic Outage
Given the dynamic scheduling service is unavailable, returns errors on 2 consecutive attempts, or exceeds 2 seconds latency When generating a confirmation or update Then send a static fallback SMS containing pickup instructions, store address, and a functional "I'm Here" link And omit window updates until health checks pass for 5 continuous minutes And on recovery, send a single catch-up update if the current window differs from the last sent by at least the threshold and throttling caps permit And log failover start/stop events and tag all fallback messages for reporting
Telemetry, Guardrails, and Rollout Controls
"As a product owner, I want auditable decisions and configurable guardrails so that we can safely roll out and prove the value of elastic slots."
Description

Capture detailed audit logs of slot decisions, including input signals, chosen window, applied guardrails, and any overrides. Provide metrics to evaluate impact (e.g., average wait, dwell, on-time rate, overbook avoidance) and dashboards for A/B or phased rollouts. Expose merchant-level configuration for min/max slot width, per-slot order caps, cooldown durations, and maximum allowable post-confirmation shift. Include a kill switch to revert to fixed 5‑minute slots instantly if issues arise.

Acceptance Criteria
Audit Log Completeness for Elastic Slot Decisions
Given an order receives a quoted pickup slot via Elastic Slots When the slot is generated or updated Then an audit log record is written within 2 seconds containing: event_type, timestamp (UTC), merchant_id, location_id, order_id, request_id, decision_id, inputs {kitchen_throughput, runner_capacity, current_dwell, backlog, constraints}, chosen_window {start, end, width_minutes}, applied_guardrails, override_flag, override_actor, reason_codes, and version And 100% of decisions in a 24-hour test window have a corresponding audit record retrievable by decision_id and request_id And the missing-field rate per required field is less than 0.1%
Outcome Metrics Pipeline Availability and Accuracy
Given Elastic Slots is enabled for a merchant When at least 200 orders are completed in a day Then the system computes and stores per-merchant and global metrics: average wait, average dwell, on-time rate, overbook avoidance count, and slot shift distribution, aggregated in 15-minute intervals And metrics are available via API and dashboard within 5 minutes of event time And metric values reconcile within 1% to a backfilled batch computation for the same period
A/B and Phased Rollout Dashboard Functionality
Given a merchant is enrolled in a 50/50 A/B test with at least 100 orders per arm in the last 7 days When a user opens the rollout dashboard and filters by location and date range Then the dashboard displays control vs treatment metrics (average wait, dwell, on-time rate, overbook avoidance) with counts and uplift deltas And cohort assignment, enrollment percentage, and sample sizes are shown And changing enrollment percentage or pausing an arm takes effect within 1 minute and is reflected in the UI And the user can export a CSV of the visible data with column headers and ISO-8601 timestamps
Merchant Guardrail Configuration via UI and API
Given a merchant admin with permissions accesses configuration via UI or API When they set min_slot_width between 5 and 30 minutes, max_slot_width between min_slot_width and 45 minutes, per_slot_order_cap between 1 and 15, cooldown_duration between 0 and 30 minutes, and max_post_confirmation_shift between 0 and 10 minutes Then valid values save successfully, take effect within 60 seconds, and are applied to new decisions And invalid values are rejected with field-level error messages and no partial saves occur And all changes are audit-logged with actor_id, old_value, new_value, timestamp, and source (UI/API)
Global Kill Switch to Fixed 5-Minute Slots
Given Elastic Slots is enabled globally When an operator toggles the kill switch to OFF Then new quotes revert to fixed 5-minute slots within 10 seconds across all merchants and locations And in-flight orders stop receiving dynamic shifts and any previously confirmed slot remains unchanged And the action is audit-logged with actor_id, reason, timestamp, and scope, and an alert is emitted And when toggled back ON, Elastic behavior resumes within 10 seconds
Staff Override Enforcement and Traceability
Given staff attempts to manually override a customer's pickup slot after confirmation When the requested change exceeds max_post_confirmation_shift or would breach per_slot_order_cap Then the system blocks the override, displays a clear reason, and suggests the nearest permissible slot And if within guardrails, the override is applied, audit-logged with actor_id and reason, and downstream notifications are sent within 5 seconds And override actions are visible in the audit trail for the order and an aggregate override rate metric is reported daily
Telemetry Degradation Fallbacks and Safety
Given any required telemetry signal (throughput, runner capacity, or dwell) is unavailable for more than 30 seconds When Elastic Slots must make a slot decision Then the system uses the last known good value within 5 minutes or merchant-configured defaults, marks the decision as degraded=true in the audit log, and enforces caps and guardrails And an alert is created for the affected merchant/location within 1 minute and cleared when recovery is detected And no slot exceeds configured max_slot_width and no per-slot cap is violated during degradation

Satellite Router

When primary bays near capacity, automatically diverts new arrivals to a pre‑mapped satellite zone. Guests get CurbNav SMS with plain‑language landmarks and a colored zone badge; staff see zone routing on the board with runner notes. Avoids lot jams, keeps handoffs flowing, and preserves near‑door bays for priority needs.

Requirements

Capacity Threshold Routing
"As a shift lead, I want the system to automatically start routing to satellites when bays are near capacity so that the lot stays fluid and near-door bays remain available for priority guests."
Description

Continuously monitors primary bay occupancy, queued arrivals, and recent handoff rates to determine when near-capacity thresholds are met. Upon trigger, automatically enables satellite routing for new arrivals, preserving near-door bays for priority orders and mobility needs. Integrates with existing bay assignment logic and respects store hours, blackout rules, and manual overrides. Configurable thresholds per location and daypart; supports soft and hard triggers with hysteresis to avoid route flapping. Expected outcome: reduced lot congestion and sustained handoff throughput.

Acceptance Criteria
Soft Threshold Enables Satellite Routing
Given a location with primary bays configured and a satellite zone mapped And the store is open and not in a blackout window And soft thresholds are configured: occupancy_soft=80%, queue_soft=3, handoff_median_soft=7 minutes, soft_hysteresis_off_occupancy=65% And no manual override is active When primary bay occupancy >= 80% for 60 consecutive seconds OR queued arrivals >= 3 for 60 consecutive seconds OR rolling median handoff time over the last 10 minutes >= 7 minutes Then the system sets Satellite Routing state to Soft On within 5 seconds And all new non-priority arrivals are assigned to the mapped satellite zone by the bay assignment engine And existing guests retain their current primary bay assignments And the state change is logged with timestamp, location_id, triggering_metric(s), measured_values, and applied_thresholds
Hard Threshold Forces Full Diversion
Given a location with primary bays configured and a satellite zone mapped And the store is open and not in a blackout window And hard thresholds are configured: occupancy_hard=95%, queue_hard=6, handoff_median_hard=10 minutes, hard_hysteresis_off_occupancy=85% And no manual override is active When any one of the monitored metrics meets or exceeds its hard threshold for 60 consecutive seconds Then the system sets Satellite Routing state to Hard On within 5 seconds And 100% of new non-priority arrivals are assigned to the satellite zone; near-door bays are not assigned to non-priority orders And the state change is logged with timestamp, location_id, triggering_metric(s), measured_values, and applied_thresholds
Hysteresis Prevents Route Flapping
Given Satellite Routing is Soft On or Hard On due to prior triggers And off-bounds are configured: soft_off (occupancy<=65%, queue<=1, handoff_median<=5 minutes) and hard_off (occupancy<=85%, queue<=4, handoff_median<=8 minutes) When all monitored metrics remain below the hard_off bounds for 120 consecutive seconds while state=Hard On Then the system transitions to Soft On if soft thresholds are currently met, otherwise transitions to Off, within 5 seconds And when all monitored metrics remain below the soft_off bounds for 120 consecutive seconds while state=Soft On Then the system transitions to Off within 5 seconds And no more than one state transition is allowed per 120 seconds per location And each transition includes a log entry with previous_state, new_state, metrics_window, and reason
Manual Override Precedence
Given a manager applies a manual override selecting Force Off, Force Soft, or Force Hard When the override is active Then the automatic evaluator does not change the Satellite Routing state And the forced state is applied within 5 seconds to all new arrivals And an audit log records override_on with actor_id, reason, selected_state, and timestamp, and repeats every 60 seconds while active And when the override is cleared, automatic evaluation resumes within 5 seconds and any state change is logged with reason=auto_resume
Store Hours and Blackout Windows Respected
Given store hours and blackout windows are configured for the location When current time is outside store hours or within an active blackout window Then automatic Satellite Routing is Off and cannot be enabled by auto triggers And any existing Soft On or Hard On state transitions to Off within 5 seconds of the window start And if a manual override Forces On during a blackout, the state changes accordingly and the log includes reason=override_during_blackout And at store open, the initial state is Off and automatic evaluation begins immediately
Priority and Mobility Orders Preserve Near-Door Bays
Given an order is flagged priority or mobility_access in the order metadata And Satellite Routing is Soft On or Hard On When the order arrives and requests assignment Then the bay assignment engine assigns a near-door primary bay if one is available regardless of Satellite Routing state And the assignment is logged with exception_type=priority_or_mobility and prior_state And the exception does not decrement satellite capacity counters
Per-Location and Daypart Config Applies
Given thresholds are configurable per location and daypart with a versioned config When the current time enters a new daypart Then the evaluator applies the active daypart thresholds within 30 seconds and logs config_version, location_id, and daypart_id And when an admin updates thresholds mid-shift Then the new thresholds take effect within 60 seconds without service restart and are reflected in subsequent trigger logs And if the active location/daypart has no satellite zone mapping, Satellite Routing remains Off and logs reason=no_satellite_zone
Satellite Zone Configuration & Map Editor
"As a location manager, I want to pre-map clear satellite zones with landmarks and colors so that guests can find them easily and staff can recognize them at a glance."
Description

Adds an admin UI to pre-map one or more satellite zones per location, define plain-language landmarks, walking route hints, photo snippets, and select a distinct color badge per zone. Supports max concurrent vehicles, staging type (curb, overflow lot, alley), and SMS template tokens. Includes drag-and-drop map pins, polygon boundaries, and validation to prevent overlap with primary bays. Data is stored per location and versioned for audit; safe preview mode before publishing.

Acceptance Criteria
Create and Publish a Satellite Zone for a Location
Given I am an authorized admin on a specific location When I open the Satellite Zone editor and enter Zone Name, select Staging Type (curb | overflow lot | alley), set Max Concurrent Vehicles (positive integer), add Landmarks and Walking Route Hint, choose a Color Badge, draw a valid polygon boundary, drop an entrance pin, and optionally attach a photo snippet (JPEG/PNG) Then the Save & Publish action becomes enabled And the photo snippet shows a thumbnail and unsupported file types are rejected with an error When I click Save & Publish Then a new zone is created under that location with all fields and geometry persisted And the zone appears in the zone list with status Active and a publish timestamp And reloading the page shows the saved values exactly as entered And the configuration is available via API for Satellite Router consumption
Validate Geometry Against Primary Bays and Other Zones
Given primary bay polygons and existing zones are loaded in the editor When I draw or edit a zone polygon that intersects, touches, or overlaps any primary bay or any other zone polygon Then Save/Publish is blocked And an error identifies the conflicting bay/zone and highlights the overlapping segments on the map When I adjust the polygon to remove all overlaps and self-intersections and ensure it encloses a non-zero area Then the error clears and Save/Publish becomes enabled When the polygon is empty or degenerates to a line/point Then Save/Publish remains disabled with an "Invalid zone boundary" error
Drag-and-Drop Map Editing (Pins and Polygon Vertices)
Given a zone boundary exists When I drag an existing vertex, add a vertex, or delete a vertex Then the polygon updates in real time and the new geometry is captured for save When I drag the entrance pin to a new location Then the entrance coordinates update and persist on save When I cancel or leave without saving Then no geometry or pin changes are persisted
Distinct Color Badge per Zone Within a Location
Given at least one zone exists in the location When I assign a color badge already used by another zone in the same location Then Save/Publish is blocked and I am prompted to choose a different color When I select an available color Then the color preview is consistent across the editor, staff board mock preview, and SMS preview
SMS Template Tokens and Safe Preview
Given the SMS template supports tokens {ZoneName}, {Landmark}, {WalkingRouteHint}, {BadgeColor}, {CurbNavLink}, {PhotoSnippet} When I click Preview Then the system renders a message substituting tokens with values from the current editor state and generates a non-routable preview CurbNav link And no SMS is sent and no live routing is affected When the template includes an unknown or unmatched token Then the preview displays a clear error and Save/Publish is disabled until corrected
Versioning, Audit History, and Rollback
Given a zone has at least one published version When I publish changes Then a new immutable version entry is created with version number, author, timestamp, and a diff of changed fields and geometry When I open History for the zone Then I can view prior versions with their metadata, compare them to the current version, and select Restore on any prior version When I restore a prior version Then a new version is created that matches the restored content and becomes the current published configuration
Arrival Auto-Assignment Engine
"As a dispatcher, I want new arrivals auto-assigned to the optimal bay or satellite zone so that handoffs are balanced and promises are met."
Description

Implements a rules-based and capacity-aware assignment engine that routes each new arrival to either a primary bay or an eligible satellite zone. Considers current occupancy, ETA, order readiness, promised time, customer mobility notes, vehicle size, and zone walking distance. Balances load across zones, enforces max capacity, and preserves near-door bays for priority needs. Produces assignment rationale for audit and displays it in staff tools; exposes hooks for future machine learning tuning.

Acceptance Criteria
Primary Bay Assignment Under Normal Load
Given a new arrival with order_readiness=ready and primary_bay_occupancy<80% and vehicle_size fits a standard bay When the "I'm here" event is received Then the engine assigns the nearest available primary bay and returns target_type=primary_bay and target_id And Then the engine does not assign a satellite_zone And Then the assignment respects bay constraints (vehicle_size, reserved=false) and does not exceed bay capacity And Then the assignment response includes rationale.considered_factors, rationale.chosen_target, rationale.rejected_candidates
Satellite Zone Diversion Near Capacity
Given primary_bay_occupancy>=90% or projected_primary_occupancy_at_ETA>=90% and at least one eligible satellite_zone exists with walking_distance<=250m and supports vehicle_size When the "I'm here" event is received Then the engine assigns the least-loaded eligible satellite_zone and returns target_type=satellite_zone, zone_id, zone_badge_color, and landmarks_ref And Then the staff_board payload includes runner_note="Route to Zone {zone_badge_color}" and zone routing details And Then the assignment does not reduce reserved near-door bays below the configured reserve_count
Priority Preservation for Mobility-Labeled Customers
Given a new arrival with mobility_note=limited or priority=true and order_readiness=ready and at least one near_door_bay is available When assignment is requested Then the engine assigns a near_door_bay regardless of primary_bay_occupancy and returns target_type=primary_bay And Then if no near_door_bay is available but one is projected to free within 3 minutes and satellite_zone walking_distance>150m Then the engine places the arrival in a priority_queue and does not divert to a satellite_zone And Then if no near_door_bay is projected within 3 minutes Then the engine assigns the closest feasible target and includes rationale.priority_override=false with justification
Max Capacity Enforcement and No-Eligible Zone Handling
Given all eligible primary_bays and satellite_zones are at capacity or violate constraints When assignment is requested Then the engine returns decision=queue with estimated_wait_minutes and reason_codes includes "no_eligible_capacity" And Then no two simultaneous assignment decisions reference the same bay_slot or zone_slot And Then upon capacity becoming available When reevaluation occurs Then the engine assigns the first-in-queue arrival that fits constraints and records the decision
Assignment Rationale Logging and Staff Display
Given any assignment or queue decision is produced When the decision is committed Then an audit_log entry is written within 1 second including arrival_id, decision_type, timestamp, chosen_target, candidate_list with scores, and factor_values, and it is retrievable for 30 days via API And Then the staff_board API payload includes rationale.summary and decision.explanation for the arrival
Load Balancing Across Satellite Zones
Given two or more eligible satellite_zones with equal constraints When 20 sequential arrivals occur with no departures Then the difference in assigned counts between any two zones is <=1 And Then zones with higher walking_distance are only selected when all lower-walking_distance zones are at capacity or violate constraints And Then in the presence of ties, the engine rotates selections to avoid consecutive assignments to the same zone
Performance, Concurrency, and ML Hooks
Given a sustained arrival rate of 50 requests per second for 5 minutes When processing assignments Then p95 decision latency is <=200ms and p99 is <=400ms And Then under 100 concurrent "I'm here" events targeting 5 bays, no duplicate assignments to the same bay occur And Then when the ML_scorer feature_flag=true, the engine calls the scorer with context (arrival_id, ETA, order_readiness, promised_time, mobility_note, vehicle_size, walking_distances, capacities, current_occupancy) and uses returned scores; when the scorer times out after 100ms or feature_flag=false, the engine uses the deterministic baseline and logs fallback And Then over a simulation of 1,000 synthetic assignments, the ML-on allocation distribution differs from baseline by <=5% per zone unless explicitly configured otherwise
CurbNav SMS with Landmarks & Colored Badge
"As an arriving guest, I want an SMS with simple directions and a colored badge to my assigned zone so that I can park correctly without calling the store."
Description

Generates an SMS-first CurbNav message that includes a short link, plain-language directions using saved landmarks, and a colored zone badge matched to the assigned satellite zone. The link opens a zone-specific 'I'm here' browser page that confirms arrival and surfaces parking guidance; no app or hardware required. Supports multilingual templates, carrier-safe length, and fallbacks for MMS/image attachments when enabled. Tracks delivery, click, and arrival confirmations for operational visibility.

Acceptance Criteria
Auto-generated SMS includes landmark directions and zone color badge
Given an order is auto-routed to a satellite zone with saved landmarks and a configured color When the CurbNav SMS is composed Then the SMS contains exactly one shortened URL using the configured short-domain And the body includes plain-language directions that reference at least one saved landmark tied to that zone And the body includes the zone name and its color as a textual badge that matches the zone configuration And validation passes that all referenced landmarks and zone colors exist and are active in configuration
Zone-specific 'I'm here' web page confirms arrival without app
Given the recipient taps the CurbNav link on a mobile device When the page loads Then it is scoped to the assigned zone and displays the same zone name and color badge as in the SMS And it shows parking guidance derived from the zone’s saved landmarks And an "I'm here" control is available without requiring app installation or login When the guest taps "I'm here" Then the system records an arrival event with order ID, zone ID, and timestamp And the staff board reflects the arrival within 5 seconds And the guest sees an on-page confirmation state
Multilingual CurbNav template selection and localization
Given the order has a preferred language code supported by the tenant When composing the CurbNav SMS Then the system renders the message using the template for that language, including localized landmark names and color terms And if any translation key is missing, only that key falls back to the default language while preserving structure And the short link and badge format are preserved across languages And the language used is recorded with the message metadata
Carrier-safe length, encoding, and segmentation control
Given the composed message text and selected language When determining encoding and length Then the system chooses GSM-7 encoding when possible, else UCS-2 And the final outbound message does not exceed the configured maxSegments for the tenant and encoding And if the message would exceed maxSegments, non-essential text is truncated before landmark names while preserving the short link, zone name, and color badge And a truncation flag with pre/post lengths is logged in message metadata And characters not supported by the chosen encoding are replaced or escaped without rendering artifacts
MMS map/badge attachment with SMS fallback
Given MMS attachments are enabled for the tenant and the recipient carrier/device supports MMS When the CurbNav message is sent Then an MMS attachment is included that displays the assigned zone color badge and a simple landmark map image And the text body still contains the short link and core directions And if MMS is not supported or delivery fails, the system automatically retries with SMS-only text within 2 seconds And the message log records whether MMS or SMS fallback was used
Delivery, link click, and arrival correlation tracking
Given a CurbNav message is sent When the carrier delivers the message Then a delivery event with timestamp is recorded When the recipient clicks the short link Then a unique click event is recorded and associated with the order and message ID When the guest confirms "I'm here" Then an arrival event is recorded and correlated to the prior send and click events And operations reporting surfaces delivery, click-through, and arrival statuses for the order via UI/API
Staff Board Zone View & Runner Notes
"As a runner, I want the board to show zone badges and runner notes so that I can stage and deliver orders efficiently."
Description

Enhances the live order board to display each guest’s assigned zone/bay with its color badge and clearly labeled routing. Adds runner notes showing approximate walk distance/time and landmark cues. Enables filtering and sorting by zone, quick reassignment between zones/bays, and visual warnings when zones near capacity. Integrates with existing staff notifications and maintains low-latency updates under peak load.

Acceptance Criteria
Zone/Bay Display with Color Badges
Given an active order has an assigned zone and bay, When the Staff Board loads or refreshes, Then the order card displays the zone name/code, bay number, and a color badge matching the zone configuration. Given a color badge is rendered, When evaluated for contrast against adjacent text/background, Then it meets WCAG AA contrast ratio of at least 4.5:1 for text and 3:1 for non-text UI elements. Given an order’s zone/bay assignment changes upstream, When the change occurs, Then the Staff Board reflects the updated zone/bay and color badge within 2 seconds at the 95th percentile.
Runner Notes with Walk Distance/Time and Landmarks
Given an order has an assigned zone, When viewed on the Staff Board, Then runner notes display estimated walk distance (feet/meters) and time (minutes) within ±20% of the configured runner pace. Given runner notes are displayed, When viewed, Then a primary landmark cue is shown and limited to 80 characters max without truncating mid-word. Given runner notes are updated due to reassignment, When reassignment occurs, Then notes update across all active sessions within 2 seconds at the 95th percentile.
Filter and Sort Orders by Zone
Given multiple active orders across zones, When a user filters by one or more zones, Then only orders in the selected zones are displayed and the visible count reflects the filtered total. Given a zone filter is applied, When the page is reloaded in the same browser session, Then the selected filter persists for that user session. Given visible orders, When sorting by Zone then Bay, Then orders are ordered alphanumerically by zone code and numerically by bay within each zone.
Quick Reassignment Between Zones/Bays
Given a user has Manage permissions, When they drag-and-drop an order card to a different zone/bay or use the Reassign action, Then the reassignment is saved and visible to all sessions within 2 seconds at the 95th percentile. Given the target zone is at or over capacity, When reassignment is attempted, Then a blocking warning is shown and the reassignment is prevented unless the user confirms an override. Given a reassignment completes, When the order is updated, Then the guest-facing assignment and runner notes are updated and an audit log entry is recorded with user, from→to, and timestamp.
Visual Zone Capacity Warnings
Given a zone has a configured capacity N, When active assigned orders reach 80% of N, Then the zone indicator displays a yellow warning with the current count as X/N. Given the zone reaches or exceeds 100% of N, When displayed, Then the indicator turns red and affected order cards show an inline capacity icon. Given orders are completed or unassigned, When utilization is calculated, Then those orders are excluded from the capacity count.
Low-Latency Board Updates Under Peak Load
Given a peak load of ≥200 active orders and ≥10 concurrent Staff Board sessions, When assignments, reassignments, or capacity changes occur, Then 95th percentile propagation to all sessions is ≤2 seconds and 99th percentile is ≤5 seconds. Given intermittent network loss, When a session reconnects within 30 seconds, Then it receives a delta sync and renders the current board state within 3 seconds of reconnection. Given multiple changes occur within 250 ms, When updates are processed, Then they are coalesced without losing events and the final state order is preserved.
Integration with Existing Staff Notifications
Given a reassignment or capacity-threshold crossing occurs, When the event is emitted, Then a notification is sent via the existing staff notification system within 3 seconds including order identifier, zone/bay, and action summary. Given duplicate events for the same order and destination within 2 seconds, When notifications are processed, Then only one notification is delivered. Given a notification fails delivery, When retries are attempted, Then it is retried up to 3 times with exponential backoff and a non-blocking alert is surfaced on the Staff Board.
Exceptions, Overrides, and Fallback Routing
"As a shift lead, I want to override or re-route assignments when conditions change so that service stays smooth and fair."
Description

Provides tools for staff to override routing, reclaim a near-door bay for a priority guest, or reassign guests if a satellite zone becomes unavailable. Detects misparked arrivals via the zone-specific 'I’m here' page and prompts guests with corrective guidance. Includes automatic fallback to primary bays when satellites are full or after-hours, with clear communication in both SMS and staff views. All actions are logged for audit.

Acceptance Criteria
Priority Guest Override to Near-Door Bay
Given a guest is currently assigned to a satellite zone and marked as priority When a staff member selects "Override to Primary Bay" and chooses an available bay or confirms "Reclaim Bay" Then the system assigns the guest to the selected primary bay within 5 seconds And updates the staff board and runner notes to reflect the new bay and priority status within 5 seconds And sends the guest an SMS with the new bay number, colored badge, and plain‑language landmark within 10 seconds And if a bay is reclaimed, the previously assigned guest is automatically rerouted per routing rules and notified via SMS And the override action is logged with user ID, timestamp, reason, previous assignment, new assignment, and notification outcomes
Satellite Zone Marked Unavailable—Bulk Reassignment
Given a satellite zone is active with one or more guests assigned or en route When a staff member marks the zone as Unavailable Then no new guests are routed to that zone immediately And all guests currently assigned to that zone are automatically reassigned to alternate satellite zones or primary bays based on capacity and priority rules And each affected guest receives an updated SMS with the new zone/bay, colored badge, and landmarks within 10 seconds And the staff board shows a reroute banner with count of affected orders and per‑guest new assignments and runner notes preserved And if no capacity exists, affected guests are placed in a virtual queue with ETA displayed to staff And all reassignment actions are logged with before/after locations, reason "Zone Unavailable", and notification outcomes
Misparked Arrival Detected via Zone‑Specific 'I’m Here' Page
Given a guest was assigned to Zone A When the guest taps an "I’m here" link for Zone B or the page zone differs from the assignment Then the system flags the arrival as Misparked and displays corrective guidance on the page to navigate to Zone A, including landmarks and colored badge And sends an SMS reminding the guest of the correct zone with guidance within 10 seconds And highlights the order on the staff board with a Misparked badge and suggested resolution And if the guest confirms arrival at the correct zone, the status clears and the correct bay/zone is recorded And the mispark event and resolution are logged with detected vs assigned zone, timestamps, and user interactions
Automatic Fallback to Primary Bays on Capacity or After‑Hours
Given satellite routing is enabled with defined capacity thresholds and operating hours When all satellite zones meet or exceed capacity or current time is outside satellite operating hours Then new arrivals are automatically routed only to primary bays And guests receive SMS that references primary bay numbers and near‑door landmarks, without satellite badges And the staff board displays a Fallback Active indicator with reason (Capacity or After‑Hours) And the system resumes satellite routing automatically once conditions clear And all fallback activations and resumptions are logged with trigger conditions and timestamps
Staff View Consistency After Overrides and Reassignments
Given one or more overrides or reassignments have occurred within the last 5 minutes When a staff member views the live board and order details Then each affected order displays the current zone/bay, colored badge, priority status, and runner notes without stale values And any superseded locations are visually shown as history, not as current And the board refreshes automatically at least every 5 seconds to reflect changes And no order shows conflicting current location between list and detail views And a manual refresh yields identical data to the auto‑refreshed view
Comprehensive Audit Log and Export for Routing Actions
Given the system has processed overrides, reassignments, fallbacks, and mispark corrections When an admin queries the audit log for a date/time range Then the result includes for each action: action type, actor (user or system), reason, before location, after location, affected order, notification outcomes, and timestamps in ISO 8601 And results can be exported to CSV within 5 seconds for up to 10,000 records And audit entries are immutable and retained per configured retention policy And viewing or exporting requires appropriate permissions and is itself logged
Satellite Performance Analytics
"As an operator, I want analytics on satellite usage and dwell times so that I can fine-tune thresholds and zones to improve throughput."
Description

Introduces dashboards and exports that report satellite usage, diversion counts, dwell time, handoff duration, zone fill rates, and deflection impact on primary bay availability. Breaks down performance by daypart and weather flags, enabling operators to tune thresholds and zone definitions. Data integrates with existing CurbPing reporting and can be filtered per location and exported as CSV.

Acceptance Criteria
Dashboard Satellite Metrics Visibility
Given a reporting-enabled user selects a location and date range When they open the Satellite Performance dashboard Then the following metrics are shown for the selected scope: total satellite arrivals, diversion count, diversion rate (%), average and median dwell time (satellite vs primary), average and median handoff duration (satellite vs primary), per-zone fill rates (%), and overall fill rate And each metric includes a tooltip with its calculation definition And values are computed from completed orders only And all timestamps and aggregations use the location’s time zone
Deflection Impact on Primary Bay Availability
Given diversion events exist in the selected scope When viewing the Deflection Impact section Then the dashboard displays availability minutes freed in primary bays and the change in average primary bay occupancy rate And the baseline is a no-diversion model that reassigns diverted arrivals back to primary bays in timestamp order And impact values reconcile against the underlying event log within 1% or 1 minute (whichever is larger)
Daypart and Weather Breakdown
Given daypart boundaries are configured for the location and weather flags are available for the period When the user enables Daypart breakdown or applies Weather flag filters Then all key metrics (diversions, dwell time, handoff duration, fill rate, deflection impact) are segmented by the selected breakdowns And per-segment totals sum to the overall value for the same scope And applying a weather flag filters metrics to only orders occurring under that flag And segment and flag labels are visible in charts and exports
Per-Location and Multi-Location Filtering
Given an account with multiple locations When the user selects one or more locations and a date range in the global filters Then all metrics, charts, and totals reflect only the selected locations and period And combined totals equal the sum of the visible per-location metrics And changing the selection updates the data and persists across navigation within Reporting
CSV Export Matches On-Screen Metrics
Given any current filters and breakdown selections on Satellite Performance When the user clicks Export CSV Then a CSV downloads containing: date/time bucket, location, zone (if applicable), diversion count, diversion rate, satellite arrivals, dwell time avg/median, handoff duration avg/median, fill rate, and deflection impact fields And all values match on-screen metrics for the same scope within rounding rules And exported data reflects the selected filters and time zone
Zone Fill Rate Calculation Accuracy
Given each satellite zone has a configured capacity value When computing fill rate for a selected period Then fill rate (%) = occupied_vehicle_minutes / (capacity_stalls × minutes_in_period) And per-zone and overall fill rates are displayed rounded to one decimal place and capped at 100% And a validation dataset with known occupancy yields the expected percentages
Integration With Existing Reporting
Given the existing CurbPing Reporting module is available When the user navigates to Reporting Then Satellite Performance appears as a selectable report integrated with shared global filters (date range, location, daypart, weather) And Satellite metrics are included in the unified enterprise export alongside other reports And Reporting permissions control access to Satellite Performance

Nudge Shift

Sends friendly, optional SMS offers to move flexible arrivals by 5–10 minutes during spikes, with configurable perks like priority bay or a small add‑on. Only targets orders that won’t impact food quality and always respects accessibility flags. Smooths the curve without pressure, cutting peak congestion for everyone.

Requirements

Real-time Spike Detection
"As an operator, I want the system to detect peak congestion periods so that demand can be smoothed automatically without constant manual monitoring."
Description

Automatically detects upcoming and live curbside pickup spikes using current order backlog, promised pickup times, historical patterns, and predicted arrival distribution. Identifies windows where shifting a subset of flexible arrivals by 5–10 minutes will lower bay congestion without creating kitchen bottlenecks or extending food hold times. Produces actionable events with target window, recommended shift delta, and max nudge count. Integrates with CurbPing’s scheduling, bay allocation, and make-time logic to ensure nudges preserve food quality windows and operational SLAs.

Acceptance Criteria
Imminent Spike Window Detection (next 30 minutes)
Given bayCapacity is configured and arrivals are binned in 5-minute slices And the prediction horizon is at least 30 minutes When predicted arrivals in any 5-minute bin exceed bayCapacity by >= 2 vehicles for >= 2 consecutive bins And kitchenAvailableThroughput for those bins is >= required tickets Then emit a SpikeDetected event within 2 seconds of detection And the event.targetWindow spans the minimal contiguous overloaded bins (rounded to 5-minute boundaries) And event.recommendedShiftDeltaMinutes is either 5 or 10 And event.maxNudgeCount is the smallest integer that reduces each overloaded bin to <= bayCapacity
Actionable Event Payload Completeness
Given a SpikeDetected event is emitted Then payload includes fields: id, idempotencyKey, targetWindow.start, targetWindow.end, recommendedShiftDeltaMinutes, maxNudgeCount, createdAt, congestionReason, predictedLoadBefore[per 5-min bin], predictedLoadAfter[per 5-min bin], kitchenThroughputSlack, affectedBays, confidenceScore, sourceModelsVersion And all timestamps are ISO-8601 UTC And recommendedShiftDeltaMinutes is in {5,10} And payload validates against the JSON schema with no missing required fields
Food Quality and SLA Preservation via Scheduling/Make-Time Integration
Given candidate flexible orders exist for the targetWindow When computing maxNudgeCount Then exclude any order whose shifted pickup time would cause foodHoldTime > allowedHoldWindow And exclude any order that would violate promisedPickupTime SLA unless explicitly marked customerFlexibleWindow=true And exclude any order with accessibilityFlag=true And for each order counted toward maxNudgeCount: predictedReadyTime <= shiftedPickupTime <= predictedReadyTime + allowedHoldWindow And integration checks with scheduling and make-time services must return OK before event emission
Kitchen Bottleneck Avoidance
Given current make queue, prep times, and historical throughput models When calculating predicted kitchen WIP with the proposed shift and maxNudgeCount Then predicted tickets-in-process per 5-minute bin <= kitchenCapacity And predicted ticketWaitTimeP90 <= targetP90 If constraints are not met Then reduce maxNudgeCount iteratively until all constraints are satisfied or maxNudgeCount = 0 If maxNudgeCount = 0 after constraint checks Then do not emit the SpikeDetected event
Real-time Refresh, Deduplication, and Cancellation
Given detection runs every 15 seconds When an existing SpikeDetected event has the same targetWindow (±1 minute) and recommendedShiftDeltaMinutes Then update the existing event by idempotencyKey instead of creating a new one When predicted load drops to <= bayCapacity for 2 consecutive bins across the targetWindow Then mark the event status as Resolved within 5 seconds and stop updates And at most 3 concurrent active SpikeDetected events may exist; additional candidates are dropped with reason=RateLimited
Eligibility and Fairness in Target Selection
Given a pool of eligible flexible orders overlapping the targetWindow When selecting orders to satisfy maxNudgeCount Then do not select more than 1 order per customer per day and not more than 2 per week And exclude orders with states in {Arrived, InBay, BeingDelivered} And exclude orders with nudgeCooldown > 0 minutes remaining And exclude orders with payment or accessibility constraints And selected orders are distributed across time buckets to avoid front-loading a single 5-minute bin And maxNudgeCount <= number of eligible orders and <= configured per-window cap
Bay Allocation and Scheduling Consistency
Given current bay allocation and scheduling snapshots When computing predictedLoadAfter for the SpikeDetected event Then per-bay occupancy per 5-minute bin <= 1 vehicle per bay And overall bay utilization per bin <= 100% of bayCapacity And event payload includes bayAllocationSnapshotId and scheduleVersion used When bay allocation or schedule versions change Then subsequent event updates recompute using the latest versions
Eligibility & Guardrails Engine
"As a guest, I want to receive nudges only when it won’t affect my needs or food quality so that I feel respected and in control."
Description

Determines which orders are eligible to receive a nudge while enforcing customer safety and quality constraints. Excludes accessibility-flagged guests, ASAP/late orders, items with narrow quality windows (e.g., fried/ice cream), orders already marked "I’m here" or within geo-fence, and customers without messaging consent or who opted out. Applies strict limits: maximum 5–10 minute shift, per-customer frequency caps, quiet hours, and store capacity thresholds. Ensures SMS compliance (opt-out language, rate limits) and provides reason codes explaining inclusion/exclusion for each order.

Acceptance Criteria
Exclusion: Accessibility-Flagged Guests
Given an order has customer.accessibilityFlag = true When the Eligibility & Guardrails Engine evaluates the order Then the order is marked ineligible for a nudge And no SMS nudge is queued or sent And reasonCodes includes "ACCESSIBILITY_FLAG" And the decision is audit-logged with orderId, customerId, reasonCodes, and timestamp
Exclusion: Arrival Already Detected
Given an order has status.imHere = true OR the customer device location is within the store geofence at evaluation time When the engine evaluates eligibility Then the order is marked ineligible for a nudge And no SMS nudge is queued or sent And reasonCodes includes "ALREADY_ARRIVED" And the decision is audit-logged with orderId, reasonCodes, and timestamp
Exclusion: ASAP or Late Orders
Given an order is marked as type = "ASAP" OR now >= order.promisedPickupTime When the engine evaluates eligibility Then the order is marked ineligible for a nudge And no SMS nudge is queued or sent And reasonCodes includes "ASAP_OR_LATE" And the decision is audit-logged with orderId, reasonCodes, and timestamp
Exclusion: Narrow Quality Window Items
Given an order contains any line item where (lineItem.requiresImmediateServe = true) OR (lineItem.qualityWindowMinutes < config.minShiftMinutes) OR (lineItem.category ∈ {"fried","ice_cream"}) When the engine evaluates eligibility Then the order is marked ineligible for a nudge And no SMS nudge is queued or sent And reasonCodes includes "QUALITY_WINDOW" And the decision is audit-logged with orderId, reasonCodes, and timestamp
Messaging Compliance: Consent, Opt-Out, Opt-Out Language, and Rate Limits
Given customer.messagingConsent != true OR customer.optedOut = true When the engine evaluates eligibility Then the order is marked ineligible for a nudge And reasonCodes includes "SMS_CONSENT" And no SMS is queued or sent Given customer.messagingConsent = true AND customer.optedOut = false When the engine prepares an SMS nudge Then the SMS body includes opt-out language "Reply STOP to opt out" And sending obeys config.smsRateLimit (per-senderId per-minute cap) And if the rate limit would be exceeded, the SMS is suppressed and reasonCodes includes "RATE_LIMIT" And all decisions are audit-logged with orderId, reasonCodes, and timestamp
Guardrail: Shift Amount Limited to 5–10 Minutes and Food Hold Window
Given an order passes all exclusion rules When the engine computes a nudge Then proposedShiftMinutes ∈ [config.minShiftMinutes, config.maxShiftMinutes] AND 5 ≤ config.minShiftMinutes ≤ config.maxShiftMinutes ≤ 10 And newSuggestedArrivalTime = originalArrivalTime + proposedShiftMinutes And estimatedHoldTimeMinutes ≤ order.allowedHoldWindowMinutes And the nudge payload includes proposedShiftMinutes and reasonCodes includes "ELIGIBLE" And the decision is audit-logged with orderId, proposedShiftMinutes, reasonCodes, and timestamp
Operational Guardrails: Quiet Hours, Capacity Thresholds, and Frequency Caps
Given now ∈ store.quietHours When the engine evaluates eligibility Then all nudges are suppressed And reasonCodes includes "QUIET_HOURS" And the decision is audit-logged Given now ∉ store.quietHours AND forecastedCapacityUtilization for the pickup window ≥ config.capacityThreshold When the engine evaluates eligibility Then capacity criteria is satisfied and reasonCodes includes "CAPACITY_THRESHOLD_MET" Given forecastedCapacityUtilization < config.capacityThreshold When the engine evaluates eligibility Then the nudge is suppressed and reasonCodes includes "CAPACITY_THRESHOLD_NOT_MET" Given customer.nudgesSentInRollingPeriod ≥ config.perCustomerFrequencyCap When the engine evaluates a new nudge for the same customer Then the nudge is suppressed and reasonCodes includes "FREQUENCY_CAP" And all decisions are audit-logged with orderId, customerId, reasonCodes, and timestamp
Incentive Configuration & Budget Controls
"As an operator, I want to set and limit the perks offered with nudges so that I can influence arrivals without hurting margins."
Description

Enables operators to configure perk types (priority bay assignment, small add-on item, loyalty points, fee waiver), eligibility rules per perk, per-day budgets, and per-order cost caps. Supports store- and chain-level templates, blackout dates, and inventory/tax handling for add-ons. Includes A/B variants for perk type and copy. Tracks redemptions, prevents perk stacking/abuse, and provides budget pacing alerts to avoid margin erosion.

Acceptance Criteria
Perk Types and Eligibility Rules Configured
Given an operator configures perk types priority bay, small add-on item, loyalty points, and fee waiver with eligibility rules by order attributes (e.g., order size, pickup window, menu tags, accessibility flag) When the configuration is saved Then each perk validates required fields and rule syntax, and invalid entries are rejected with field-level errors And the configuration is persisted with a timestamp and user id Given an incoming order matches the defined eligibility rules for any configured perk When a nudge is evaluated for that order Then only perks whose rules evaluate to true and do not violate accessibility flags or food-quality constraints are eligible And orders with accessibility_flag = true are not targeted for time-shift nudges or incentives
Daily Budgets, Per-Order Caps, and Pacing Alerts
Given a store daily incentive budget of $100 and a per-order cost cap of $2.50 When offers are sent and redeemed Then cumulative incentive cost for the store does not exceed $100 for the day And any perk whose cost would exceed $2.50 for a given order is not offered; the system selects the next eligible perk under cap or sends no perk if none qualify Given remaining daily budget is less than the minimum cost of any eligible perk When evaluating a new order Then no perk is offered and the decision is logged with reason "budget_exhausted" Given budget consumption rate in the last 60 minutes projects end-of-day spend > 110% of daily budget When the projection threshold is crossed Then a pacing alert is sent within 2 minutes via configured channels (email/SMS/webhook) and alerts are throttled to at most one every 30 minutes per store Given the daily reset time is reached When the new business day starts Then budgets automatically reset and paused perks resume eligibility evaluation
Store and Chain Templates with Overrides
Given a chain-level incentive template exists with default perks, rules, budgets, and copy When a store inherits the template Then the store sees all fields as read-only until override is enabled Given the store enables override for specific fields When the store saves changes Then only overridden fields differ from the chain template; non-overridden fields continue to inherit updates from the chain And effective dates on templates control when configurations become active; future-dated templates activate at the start time and prior templates are archived
Blackout Dates and Dayparts Enforced
Given blackout dates and/or dayparts are configured at chain or store level When current time falls within a blackout window Then no nudge or incentive is sent, queued, or displayed for eligible orders And any scheduled or queued messages for the blackout window are canceled with reason "blackout" When current time exits the blackout window Then normal eligibility evaluation resumes without requiring manual intervention
Add-On Inventory and Tax Handling
Given a small add-on item perk is configured and mapped to SKU ABC with on-hand inventory and tax rules configured When a customer redeems the add-on perk Then inventory for SKU ABC decrements by 1 and cannot drop below 0; if on-hand is 0, the perk is not offered and an alternative eligible perk is considered And the order reflects the add-on as a comped line item using the store’s tax configuration for comps; tax amounts are calculated accordingly and exported to POS/order reports And a redemption event is logged including order_id, phone, perk_type, variant_id, SKU, unit_cost, tax_amount, and timestamp
A/B Variants and Allocation
Given two variants A and B are configured with a 60%/40% allocation and distinct perk types and SMS copy When 500 or more eligible orders are evaluated in a store-week Then assignment rates fall within ±3 percentage points of the configured split And each phone number is sticky to its assigned variant for 30 days And reporting surfaces impressions, sends, redemptions, cost per redemption, and uplift by variant at store and chain levels
Redemption Tracking, Anti-Stacking, and Abuse Prevention
Given a single order and phone number When multiple incentive links are clicked or SMS is resent Then at most one perk is applied per order; duplicate redemptions are rejected with a user-facing message and logged with reason "already_redeemed" Given a phone number or loyalty id reaches a daily limit of 1 redemption and a weekly limit of 3 for the same perk type (configurable) When additional redemptions are attempted within the limits window Then the perk is not applied and the attempt is logged with reason "frequency_cap" Given cross-store activity within the same chain When the same order_id or phone attempts to redeem across locations within 24 hours Then deduplication prevents stacking across stores And an abuse rules report is available showing blocked attempts by reason with counts and phone hashes
SMS Nudge Messaging & Delivery
"As a guest, I want a clear text with simple accept or decline options so that I can help during busy times without hassle or commitment."
Description

Generates friendly, low-pressure SMS offers that clearly present the optional 5–10 minute shift, perk details, and one-tap accept/decline links with expiry. Supports personalization, multilingual templates, quiet hours, carrier-compliant opt-out language, and sending rate limits. Integrates with existing SMS provider, uses trackable short links, and logs delivery, clicks, and replies for observability and auditing.

Acceptance Criteria
Compose and Send Compliant Nudge SMS
Given an eligible order (no accessibility flags, food-quality safe) with customer first name, language preference, a configured perk, an offered shift of 5–10 minutes, offer TTL of 20 minutes, and an opted-in E.164 mobile number When a nudge is triggered outside quiet hours Then the SMS is sent via the configured provider within 5 seconds using the matching language template And the message states the shift is optional and that declining will not affect service And the message clearly states the 5–10 minute shift and includes the perk details when configured And the message contains two distinct trackable short links for Accept and for Decline And the message includes carrier-compliant opt-out language: Reply STOP to opt out And all personalization tokens render with graceful fallbacks if any field is missing And the send is logged with message_id, order_id, phone_e164, template_id, language, perk_id (or null), accept_link_id, decline_link_id, ttl_seconds, and timestamp
Quiet Hours Suppression
Given quiet hours are configured from 21:00 to 08:00 local time When a nudge would be sent at 21:15 Then no SMS is sent And the suppression is logged with order_id, reason quiet_hours, and timestamp And no customer-facing follow-up is sent for this suppressed offer
Rate Limiting and Queueing
Given the tenant-level sending rate limit is set to 30 SMS per minute When 90 nudge messages are triggered simultaneously Then the provider send rate never exceeds 30 SMS per minute And all 90 messages are delivered within 3 minutes of the trigger time And messages are dequeued in FIFO order by trigger time And no recipient receives a duplicate message for the same offer And rate-limit metrics (queued_count, send_rate_per_minute, oldest_queue_age_ms) are emitted
One-Tap Accept/Decline and Expiry
Given an offer with a TTL of 20 minutes and unique Accept and Decline links When the recipient taps Accept within the TTL Then the acceptance is recorded with order_id, link_id, and timestamp And the offered arrival shift is applied to the order And staff are notified of the updated ETA And a confirmation SMS is sent to the recipient When the recipient taps Decline within the TTL Then the decline is recorded and no schedule change occurs And no further nudges are sent for that order And a friendly confirmation SMS is sent When either link is used after the TTL Then no schedule change occurs and an offer expired page is shown And the expired click is logged And no confirmation SMS is sent
Keyword Reply Handling (YES/NO/STOP/HELP)
Given an active offer with time remaining before expiry When the recipient replies YES Then process the reply as an acceptance exactly as if the Accept link was tapped When the recipient replies NO Then process the reply as a decline exactly as if the Decline link was tapped When the recipient replies STOP at any time Then immediately opt the number out, send a carrier-compliant STOP confirmation, and halt any further sends When the recipient replies HELP Then send a HELP response including the business name and support contact information When the recipient sends an unrecognized reply during an active offer Then send a single fallback message prompting to use the links or reply YES/NO within the current TTL And additional unrecognized replies within 24 hours do not trigger more fallback messages
Delivery Receipts, Retries, and Idempotency
Given the SMS provider returns a transient 5xx error on send When sending the message Then the system retries up to 3 times with exponential backoff (1s, 3s, 9s) And the same idempotency key is used so that at most one message is delivered And the final outcome (delivered or failed) is logged with provider_status and timestamp When a delivery receipt is received Then the message record is updated to Delivered with receipt timestamp and provider_message_id When the provider returns a permanent 4xx error Then the message is not retried and is logged as Failed with error_code and timestamp
Multilingual Template Selection and Fallback
Given templates are available for English (en) and Spanish (es) with identical placeholders When the recipient preference is es Then the SMS is composed and sent using the es template with all placeholders resolved When the recipient preference is an unsupported language (e.g., fr) Then the SMS falls back to the default en template And the language and fallback decision are logged with template_id, requested_language, resolved_language, and timestamp And template validation prevents sending if any required placeholder is missing in the selected language
Response Handling & Schedule Update
"As a staff member, I want the system to automatically update arrival and bay plans after a guest responds so that operations stay synchronized without manual effort."
Description

Processes guest responses in real time to update promised pickup times, adjust bay priority assignment, and recalculate kitchen make-times to keep food hot. Sends confirmation/thank-you SMS, updates staff dashboards, and enforces guardrails to avoid over-shifting. Handles edge cases including early arrival after accepting, expired offers, cancellations, and conflicting updates. Ensures all changes are auditable with timestamps and reason codes.

Acceptance Criteria
Accepted Nudge updates pickup time, bay priority, and kitchen make-times in real time
Given an active Nudge Shift offer for order O with a proposed +5–10 minute shift and a customer reply of YES within the offer window When the response is received and validated Then the system updates promised_pickup_time by the offered amount within 1 second of receipt And recalculates kitchen make-times so predicted ready_time is 0–5 minutes before the new promised_pickup_time And updates bay assignment priority to the configured perk level (e.g., priority bay) without displacing accessibility bays And updates staff dashboards (order card, queue, bay list) within 2 seconds to reflect new times and priority And sends a confirmation/thank-you SMS including the new time and perk within 3 seconds And emits an event order.nudge.accepted with order_id, delta_minutes, actor=guest, and correlation_id within 1 second And creates an audit record with ms-precision timestamps, old→new values, actor=guest, reason_code=NudgeAccepted, and source=SMS
Declined or no-response Nudge makes no schedule changes
Given an active Nudge Shift offer for order O When the customer replies NO within the offer window Then no changes are made to promised_pickup_time, bay priority, or kitchen make-times And a polite acknowledgment SMS is sent within 3 seconds And the offer is closed and removed from staff dashboards within 2 seconds And an audit record is created with reason_code=NudgeDeclined And an event order.nudge.declined is emitted Given an active Nudge Shift offer for order O When the offer window expires with no reply Then no changes are made to schedule or priority And no SMS is sent unless configured for gentle reminder off; if reminder on, send final timeout SMS once And an audit record is created with reason_code=NudgeNoResponse And an event order.nudge.expired is emitted
Guardrails block over-shifting and quality/safety breaches
Given a customer replies YES to a Nudge Shift offer When applying the shift would violate any guardrail (max_shift_per_order ≤10 min, max_shift_accepts_per_15min_window, bay_capacity > 90%, predicted hold_time > 10 min, accessibility_flag=true, or kitchen_throughput_limit exceeded) Then the shift is rejected and no schedule or priority changes occur And an apology SMS is sent within 3 seconds stating the original pickup time remains And staff dashboards show the offer as Rejected (Guardrail) with the violated constraints listed And an audit record is created with reason_code=NudgeRejectedGuardrail and violated_constraints array And an event order.nudge.rejected_guardrail is emitted
Early arrival after accepting a later Nudge is handled gracefully
Given a customer previously accepted a +5–10 minute Nudge And the customer triggers the "I'm here" arrival link earlier than the new promised time When the arrival is detected Then the system updates promised_pickup_time to now (±1 minute) and assigns the nearest available bay within 30 seconds And kitchen make-times are re-sequenced so the order is prioritized to be ready within ≤5 minutes if not yet ready And if the order is already ready, hold_time is kept ≤10 minutes and staff are alerted to expedite handoff And a confirmation SMS with bay number and updated pickup time is sent within 3 seconds And staff dashboards reflect the arrival and bay assignment within 2 seconds And an audit record is created with reason_code=EarlyArrivalAfterNudge and previous_nudge_delta
Late reply to an expired offer does not alter schedule
Given a Nudge Shift offer for order O expired at time T When the customer replies YES or NO after T Then no changes are made to promised_pickup_time, bay priority, or kitchen plan And an SMS is sent within 3 seconds informing the guest the offer expired and reiterating the current pickup time And an audit record is created with reason_code=OfferExpiredResponse and reply_timestamp And an event order.nudge.late_response is emitted And, if configured, the system re-evaluates eligibility for a new offer without sending any immediate change to the guest
Cancellation after Nudge acceptance releases resources and notifies stakeholders
Given an order with an accepted Nudge Shift When the order is cancelled by guest or staff Then any reserved/priority bay assignment is released within 1 second And kitchen plan is updated to cancel or minimize waste (e.g., stop cook if not fired, repurpose if allowed) And staff dashboards show the order as Cancelled with previous nudge details And a cancellation confirmation SMS is sent within 3 seconds And further replies to the previous offer receive an automated "order cancelled" message And an audit record is created with reason_code=OrderCanceledAfterNudge and cancellation_source And an event order.cancelled is emitted with prior_nudge context
Deterministic conflict resolution between staff edits and guest responses
Given staff manually updates promised_pickup_time at server_received_at=T1 And the guest accepts a Nudge at server_received_at=T2 within an overlapping window When both updates target the same order O Then the system applies deterministic resolution where the latest server_received_at wins And the final promised_pickup_time, bay priority, and kitchen plan are consistent across API, dashboards, and SMS within 3 seconds And the losing action triggers a clarification SMS to the guest with the final time (only if the losing action was guest-facing) And both actions are recorded in a single correlated audit trail with correlation_id, reason_codes (StaffManualEdit, NudgeAccepted), and old→new diffs And only one final state transition occurs (no oscillation), enforced via atomic transaction/ETag
Staff Dashboard Controls & Visibility
"As a manager, I want real-time visibility and simple controls for nudges so that I can maintain service quality during peaks."
Description

Adds real-time dashboard modules showing current spikes, sent nudges, acceptance rates, shifted ETAs, and bay utilization impact. Provides controls to pause/resume nudges, adjust aggressiveness within safe limits, and temporarily exclude specific bays or order types. Clearly surfaces accessibility flags and the reasons orders were not nudged. Issues alerts when budgets approach limits or when quality windows would be violated.

Acceptance Criteria
Real-Time Spike Visualization on Staff Dashboard
- The dashboard displays a Current Spike module when forecasted arrival load >120% of the trailing 30‑minute same‑weekday baseline for any of the next 10 minutes. - The module shows: load index, affected time window, pending order count, nudgeable order count, nudges sent in last 15 minutes, acceptance rate, and average shift (minutes). - Data freshness: values update at least every 5 seconds; dashboard values match backend metrics within ±3% or ±2 orders (whichever is larger). - When no spike is active, the module shows No active spike and the last update timestamp within 5 seconds of the latest backend tick. - Clicking the module reveals a breakdown by bay and order type within 1 second, with totals matching the summary within ±1 order.
Pause and Resume Nudge Shift from Dashboard Controls
- A single toggle control switches the Nudge Shift system between Active and Paused states; visual state change occurs within 300 ms of user action. - Functional effect: no new nudge SMS are queued or sent more than 1 second after entering Paused; Resume re-enables queuing immediately. - Pending but unsent nudges are canceled upon Paused and displayed as Canceled in the activity list within 2 seconds. - State persists across page reloads and new sessions; the persisted state reflects in the UI within 2 seconds of load. - A confirmation toast appears on Pause/Resume with timestamp and actor; the toast auto-dismisses in 4–8 seconds and the event is recorded in the session activity panel.
Adjust Nudge Aggressiveness Within Safe Limits
- An aggressiveness control allows selecting a deferral between 0 and 10 minutes in 1‑minute increments and a target nudge rate cap (0–30% of eligible orders). - Safe limits are enforced: selections that would breach food quality windows or accessibility constraints are blocked with an inline error; Save is disabled in that state. - Configuration changes propagate to targeting within 2 seconds; the effective settings and timestamp display next to the control. - The dashboard updates predicted acceptance rate, average shift, and projected bay utilization deltas within 2 seconds of a change. - No configuration permits nudging orders with less than 6 minutes of lead time remaining or within a quality window that would cause food degradation, as defined by menu item rules.
Temporarily Exclude Specific Bays and Order Types from Nudging
- Users can select one or more bay IDs and/or order types (e.g., Large, Catering, Hot-only) to exclude via a control supporting predefined durations (15/30/60 minutes) and a custom end time. - Exclusions take effect within 2 seconds: eligible order counts and targeting immediately reflect the exclusions, and no nudges are sent to excluded categories during the active window. - Active exclusions are displayed as labeled chips with countdown timers and can be removed individually; removal effects apply within 2 seconds. - Orders already nudged before an exclusion remain unaffected; no duplicate or retract messages are sent. - Summary metrics annotate Excluded categories so totals reconcile: Eligible = Total − Ineligible(Accessibility, Quality, Budget, Exclusions).
Surface Accessibility Flags and Non-Nudge Reasons per Order
- Each order card shows an Accessibility icon when flagged; such orders are always marked Not Nudgeable and any nudge action is disabled with an explanatory tooltip. - For orders not nudged, a standardized reason code is shown: AccessibilityFlag, QualityWindowRisk, BudgetCapReached, ExcludedBay, ExcludedOrderType, AlreadyArrived, LowLeadTime, CustomerOptOut. - A filter allows listing only Not Nudged orders by reason; applying the filter updates the list within 1 second and counts reconcile with the summary. - Reason codes and accessibility flags match backend determinations with 100% accuracy for the current session. - Hover or tap reveals the computed rule that led to the reason (key threshold and timestamp) within 300 ms.
Budget Threshold and Quality Window Violation Alerts
- The dashboard issues a Warning alert when daily nudge budget consumption reaches 80% and a Critical alert at 100%; the Critical alert automatically halts new nudges until budget is increased or the day rolls over. - A blocking alert is shown if a proposed nudge cohort would violate any menu item’s quality window; such nudges are prevented from sending. - Alerts contain: type, current value vs limit, affected scope (bays/items), suggested actions (pause, reduce aggressiveness, lift budget), timestamp, and actor if user‑initiated. - Alerts are visible within 2 seconds of the triggering condition and persist until dismissed; dismissed alerts remain accessible in the session alert list for 24 hours. - Metrics displayed during an alert continue updating at normal cadence and remain within ±3% of backend values.
Bay Utilization Impact Metrics and Shifted ETAs
- The dashboard shows current per‑bay utilization (% occupied or assigned) and a forecast for the next 15 minutes with and without nudges, including the delta. - It displays: nudges sent (last 15/60 minutes), acceptance rate, average ETA shift (minutes), and the number of orders moved out of the peak window. - Metrics update at least every 10 seconds and match backend computation within ±5% for percentages and ±1 minute for average shifts. - Excluded bays are denoted and excluded from aggregate impact calculations; a toggle allows including them for comparison. - Clicking a bay reveals a time‑series of utilization and accepted shifts for that bay within 1 second, with totals reconciling with the summary.
Impact Analytics & Experimentation
"As a product owner, I want to measure the operational and customer impact of Nudge Shift so that we can optimize rules and validate ROI."
Description

Delivers reports quantifying peak reduction, average wait time change, staff minutes saved, conversion rate, total minutes shifted, food quality incident trend, CSAT/NPS movement, opt-out rates, and revenue/margin impact of perks. Supports cohorting by store, daypart, item category, and A/B variant. Provides export and API access for BI tools and retains experiment metadata for longitudinal analysis.

Acceptance Criteria
Impact dashboard shows required Nudge Shift metrics for selected period
Given an analytics admin selects a completed Nudge Shift experiment and a 30-day date range When the dashboard loads Then it displays metrics: peak reduction %, average wait time change (min), staff minutes saved, conversion rate, total minutes shifted (min), food quality incident trend, CSAT/NPS movement, opt-out rate, revenue impact, and margin impact And each metric shows experiment vs baseline with absolute and % deltas And the default baseline is the 30 days prior to experiment start unless a custom baseline is selected And metric values are rounded: time to 0.1 min, rates to 0.1 percentage points, money to cents And each metric includes a tooltip with the definition and formula
Cohort filters by store, daypart, item category, and A/B variant
Given multiple stores, configured dayparts, item categories, and at least two A/B variants exist When any single filter is applied Then totals and order counts update within 2 seconds And counts equal the sum of visible cohort rows When multiple filters are combined Then results reflect the intersection of selected filters And daypart boundaries honor each store’s timezone And a Clear Filters action resets to global totals
A/B experiment attribution and significance are correct
Given an experiment with Control and at least one Treatment and assignment logs with order_ids and assignment timestamps When computing conversion rate, total minutes shifted, and opt-out rate by variant Then metrics use intent-to-treat cohorts based on assignment time before arrival And orders that opted out remain in their assigned cohort And accessibility-flagged orders are excluded from targetable counts but included in global totals when Include non-targetable is enabled And a two-tailed z-test at alpha=0.05 is computed for conversion and minutes shifted with 95% confidence intervals displayed And sample sizes, lifts, and p-values are shown per variant
Export and API deliver analytics to BI tools
Given a user with export permission requests analytics for a date range and selected cohorts When exporting from the UI Then downloadable CSV and Parquet files with schema v1.0 are generated and row counts match on-screen totals And files include columns: store_id, order_id, assignment_variant, arrival_ts, wait_minutes, shifted_minutes, opted_out, food_quality_incident, csat, nps, perk_cost, revenue, margin, experiment_id, experiment_version And exports complete within 5 minutes for up to 100k rows When retrieving via API Then GET /v1/experiments/{id}/analytics returns within 2 seconds for up to 10k rows per page with pagination links And API requires OAuth2 scope analytics.read and returns HTTP 429 when rate limit exceeded
Experiment metadata retention and lineage support longitudinal analysis
Given multiple experiments and versions over 12 months When retrieving metadata for any experiment Then the following fields are present: id, name, start_ts, end_ts, status, variants, targeting_rules_hash, sms_template_hash, perk_catalog_version, bay_policy_version, created_by, last_modified_by And metadata is immutable after experiment lock except end_ts and status And lineage links prior versions via parent_experiment_id And metadata and associated analytics are retained and queryable for at least 24 months And any change to messaging or perks creates a new experiment version
Data freshness, accuracy, and reconciliation
Given live operations and streaming events When viewing today’s dashboard Then analytics latency is less than or equal to 5 minutes behind real time And late-arriving events are backfilled within 24 hours and totals reconcile automatically And duplicate events are deduplicated by (order_id, event_type, ts) with idempotent reprocessing And all timestamps are stored in UTC and displayed in store local time with DST handled And metric aggregates reconcile to underlying events within +/- 0.5%
Revenue and margin impact of perks computed correctly
Given orders with recorded perk costs and revenues across variants When calculating financial impact Then revenue impact equals incremental revenue vs baseline summed over the cohort And margin impact subtracts perk costs and discounts from revenue impact And a breakdown table shows cost-of-perk and margin impact by item category And 95% confidence intervals are displayed for revenue and margin impact And negative-margin cohorts are flagged when margin delta is below 0

Fire Curve

Aligns kitchen fire/hold timing to the shaped arrival curve so orders finish just as bays free up. Expo sees a simple timeline of when to fire, hold, or stage by zone; Captains get readiness cues synced to slot load. Fewer remakes, hotter food, and tighter handoffs during surges.

Requirements

Prep Profiles & Hold Windows
"As a kitchen manager, I want accurate prep-time and hold profiles for each menu item so that Fire Curve can schedule fires and holds that keep food hot without over-holding."
Description

Define and manage per-item prep-time profiles, hold tolerances, packaging time, and station/line constraints to drive accurate fire and hold planning. Support batchable items, hot/cold separation, and staging zone definitions. Provide a lightweight admin UI in the CurbPing console with import/export, versioning, and per-location overrides. Continuously calibrate prep times using observed production data to improve accuracy over time. Integrate with the menu master so Fire Curve can compute target fire time, hold window, and staging time for each order component.

Acceptance Criteria
Create and Edit Prep-Time Profile for Menu Item
Given I am an authorized console user with access to Menu > Prep Profiles When I create a new profile for an existing menu master item or edit an existing profile Then I can set and save: base prep time (seconds), packaging time (seconds), hold tolerance (minutes), station/line assignment, hot/cold flag, batchable flag with batch size and cycle time, and staging zone And required fields are validated with inline errors and blocked save if invalid And the saved profile is persisted and retrievable within 2 seconds and reflects in the profile list with last-updated metadata
Import and Export Prep Profiles
Given I have a CSV or JSON file matching the documented schema with menu master item IDs When I import the file via the console Then the system validates the schema, shows a preview of changes, and on confirm upserts profiles atomically And invalid rows are rejected with row-level error messages and a downloadable error report And the import is idempotent and processing 5,000 rows completes within 120 seconds And I can export the current active profiles for a scope (brand or location) as CSV or JSON with identical schema
Profile Versioning and Rollback
Given profiles exist for a brand and locations When I publish a new version for one or more items Then a new version number is created, the previous version is archived, and exactly one version is active per location scope And I can schedule an effective date/time or make it effective immediately And I can view a field-by-field diff and rollback to any prior version with a single action And the audit log records user, timestamp, affected items, old/new values, and scope
Per-Location Overrides and Precedence
Given a brand default profile exists for an item When I create a location-specific override Then the location override takes precedence for that location while other locations continue using the brand default And clearing the override reverts the location to the current brand default without data loss And the compute service always resolves to the correct active version by location and timestamp
Continuous Calibration of Prep Times
Given calibration is enabled for an item When the system ingests completed order production timestamps meeting the configured sample threshold Then the baseline prep time is adjusted using outlier-resistant aggregation, capped by a configurable maximum daily change, and bounded within min/max limits And calibration updates create a new profile revision with rationale and metrics (n, median, p90) in the audit log And I can opt out specific items or stations from calibration and lock their values
Fire Curve Compute Inputs for Order Components
Given Fire Curve requests timing for an order with promised handoff time and item list When the service evaluates prep profiles, hold tolerances, packaging, station constraints, and staging zones Then for each component it returns target fire time, packaging start, staging start, and hold window start/end And hot and cold items are sequenced to finish within their respective hold tolerances without co-mingling And if constraints make targets infeasible, the response includes a capacity warning and the least-violation plan
Batchable Items and Station Capacity Planning
Given an item is marked batchable with batch size and cycle time and a station has a max concurrent capacity When multiple orders containing that item are pending for overlapping windows Then the system groups items into batches up to batch size and schedules batch fires to satisfy earliest required hold windows And planned fires do not exceed station concurrent capacity; overflow is flagged with warnings and adjusted timings And batch completion timestamps are propagated to all linked order components
Arrival Curve Engine
"As an expo lead, I want a real-time plan that predicts arrivals and suggests when to fire or hold items so that orders finish just as bays free up."
Description

Compute a shaped arrival curve that predicts when customers will reach bays using promised times, historical drive patterns, SMS 'I’m here' link pings, and bay turnover rates. For each order, output target fire time, hold window, and staging moment based on item prep profiles and station capacity. Continuously update in real time as customer signals and kitchen progress change. Integrate with CurbPing order feed, bay assignment, and SMS events without requiring apps or extra hardware.

Acceptance Criteria
Compute Shaped Arrival Curve from Multi-Source Signals
Given active curbside orders with promised ready times, historical drive patterns, current bay occupancy/turnover rates, and any SMS "I'm here" pings, When the engine runs, Then it computes a per-order predicted bay-arrival timestamp and a store-level arrival curve for the next 60 minutes. Then each prediction includes the feature sources used and a confidence score between 0 and 1. Then the per-minute arrival counts are non-negative and the integral of the curve over the horizon equals the number of predicted arrivals within that horizon. Then orders without live signals default to promised time plus a learned historical bias specific to store/daypart. Then each recomputation cycle completes within 1 second under a load of up to 200 active orders and 20 bays.
Real-Time Recalculation on New Signals
Given a new event (new order, SMS "I'm here" ping, kitchen progress update, or bay state change), When the event is received, Then affected per-order predictions and the store arrival curve are recomputed and published within 500 ms p50 and 1,500 ms p95. Then downstream subscribers receive a versioned update with an incremented sequence number and lastUpdated timestamp. Then recomputes are debounced to at most one recompute per 100 ms per store while ensuring all events are reflected within 2 seconds. Then updates are idempotent; reprocessing the same event produces the same outputs and does not create duplicate schedule entries.
Fire, Hold, and Stage Schedule Generation
Given item-level prep profiles and station capacities, When an arrival curve exists, Then for each order the engine outputs target fire time, hold window [start,end], and staging moment aligned to predicted bay availability. Then scheduled fires per station per minute do not exceed capacity; overflow is deferred using least-impact heuristics while minimizing lateness. Then hold windows meet or exceed each item’s minimum hold time and do not exceed quality thresholds; orders projected to exceed are flagged as risk:quality. Then staging moments are set 2–4 minutes before predicted arrival unless bay congestion necessitates adjustment. Then all schedule outputs include time zone, orderId, stationId (where applicable), and zoneId metadata.
Integration with CurbPing Order Feed, Bay Assignment, and SMS
Given the CurbPing order feed, bay assignment service, and SMS gateway events, When connected, Then the engine consumes these inputs via documented topics or REST endpoints without requiring any customer apps or additional hardware. Then it emits outputs to a Fire Curve timeline stream and a REST endpoint (/schedule) containing per-order schedules and per-zone load projections. Then correlation uses shared orderId and bayId; mismatched or missing IDs are rejected with explicit error codes and retried with exponential backoff. Then the engine continues operating if any single upstream is unavailable by using last known state for up to 10 minutes and marking data as stale.
Prediction Accuracy and Stability
Given ground-truth bay arrival timestamps after handoff, When evaluating a rolling 14-day window with at least 500 orders, Then MAE of predicted arrival time is ≤ 1.5 minutes for orders with an "I'm here" ping and ≤ 3.5 minutes for orders without a ping. Then the 95th percentile absolute error is ≤ 5 minutes across all orders. Then successive prediction updates for a single order change by no more than 60 seconds within any 30-second interval unless a new signal arrives for that order. Then arrival-curve aggregate error versus actual arrivals per 5-minute bucket has MAPE ≤ 25% during peak hours and ≤ 15% off-peak.
Graceful Degradation and Recovery
Given missing inputs (no historical profiles, no bay turnover data, or SMS outage), When the engine operates, Then it falls back to a baseline model using promised times plus a configurable buffer and publishes a degraded flag. Then service restarts recover the latest arrival curve and per-order schedules from durable storage within 10 seconds and resume updates without manual steps. Then data gaps are backfilled when sources return, with recomputed predictions marked with a higher version and reason codes. Then event processing guarantees at-least-once delivery; no event loss occurs under normal conditions, and duplicate events do not alter final outputs.
Expo Fire-Hold Timeline
"As an expo, I want a simple timeline that tells me exactly when to fire, hold, or stage by zone so that I can coordinate the line and minimize remakes."
Description

Deliver a concise, color-coded timeline UI for expo showing per-order and per-zone actions (fire, hold, stage) over the next 30 minutes. Support filtering by zone/station, item readiness, and lateness risk, with live countdowns and gentle urgency cues. Allow safe manual adjustments (e.g., drag to delay fire) that respect prep-time and hold constraints, with instant recomputation by Fire Curve. Designed for tablet and desktop in the existing CurbPing web console with low-latency updates and accessibility standards.

Acceptance Criteria
Expo Timeline: 30-Minute Color-Coded View
Given the expo is on the CurbPing web console timeline view When there are active curbside orders with Fire Curve recommendations within the next 30 minutes Then the timeline renders a 0–30 minute horizon with a moving Now marker and minute ticks And each order is displayed in its zone swimlane with a color-coded action state (Fire/Hold/Stage) per design tokens And a legend is visible mapping colors and icons to actions And orders beyond 30 minutes are not shown; orders within 30 minutes are shown in full And hovering or focusing an order reveals a tooltip with order ID, zone, recommended action window, ETA, and prep/hold times
Filtering by Zone/Station, Readiness, and Lateness Risk
Given the filter panel is visible When the user selects one or more Zones and/or Stations Then only orders in the selected Zones/Stations remain visible and counts update accordingly When the user filters by Item Readiness (Ready, In-Prep, Unknown) Then only orders matching the selected readiness states are visible When the user filters by Lateness Risk (Low, Medium, High) Then only orders with the selected risk levels are visible And multiple filters combine with AND logic And clicking Reset restores default (All) filters And applying or clearing any single filter updates visible results within 200 ms for up to 200 concurrent orders
Live Countdowns and Gentle Urgency Cues
Given an order has a recommended action time from Fire Curve When the timeline is displayed Then each order shows a live countdown to its next action, updating at 1-second intervals and accurate to ±1 second And urgency cues escalate as thresholds are crossed (e.g., <2 min = medium emphasis, overdue = high emphasis) without flashing or audio And urgency is conveyed with both color and icon/label so information is not color-only And lateness risk badges (Low/Medium/High) are visible and update as Fire Curve recomputes
Safe Manual Adjustments with Instant Recompute
Given the user has permission to adjust schedules When the user drags an order’s Fire time on the timeline Then the UI constrains the drop target to not violate minimum prep time or maximum hold window and shows validation messaging if blocked And on a valid drop, Fire Curve recomputes affected recommendations and the timeline updates within 500 ms And an Undo action is presented for 10 seconds to revert the change And the adjustment is audit-logged with user, order ID, previous time, new time, and delta And the same adjustment is possible via keyboard (focus order → adjust earlier/later in 1-minute increments)
Low-Latency, Real-Time Updates and Resilience
Given the client is connected to real-time updates When bay assignments, arrivals, or preparation statuses change Then recommendation changes propagate to the timeline with p95 end-to-end latency ≤ 1.5 seconds and p50 ≤ 500 ms And concurrent changes made by another user become visible locally within 2 seconds And if the connection is lost for >5 seconds, an offline banner appears and the client auto-retries until reconnected; upon reconnect, state is resynced within 2 seconds
Accessibility Compliance (WCAG 2.1 AA)
Given a user relies on assistive technologies or keyboard-only navigation When using the timeline Then all interactive elements are reachable in a logical tab order with a visible focus indicator And all timeline items, controls, and urgency cues have accessible names/roles/states for screen readers, including aria-live polite announcements for time-critical changes And color contrast meets WCAG 2.1 AA (text ≥4.5:1, large text ≥3:1) and information is not conveyed by color alone And a non-drag keyboard alternative exists for all drag actions And motion/animation respects prefers-reduced-motion, providing equivalent non-animated cues
Responsive Tablet and Desktop Behavior
Given the CurbPing web console is opened on desktop (≥1280×800) or tablet (≥1024×768 touch) When viewing and interacting with the timeline Then layout adapts without horizontal scrolling for up to 6 zone swimlanes; additional zones are vertically scrollable with sticky headers And touch targets on tablet are ≥44×44 px and gestures (drag, scroll) do not conflict with page scroll And initial render completes within 1 second for 100 visible orders and maintains smooth scrolling at ≥60 FPS
Bay Load Synchronization
"As a curbside coordinator, I want fires aligned to bay availability so that completed orders are handed off immediately without clogging staging."
Description

Synchronize the fire/hold plan with numbered bay occupancy. Track bay check-ins via the browser 'I’m here' link, forecast when bays will free, and align order readiness to the projected slot availability. Reserve or suggest bays for imminent ready orders, handle misparks with zone remapping, and adjust the plan when actual bay turnover deviates. Integrate with current bay assignment and SMS instructions to direct customers to the optimal bay in real time.

Acceptance Criteria
Real-time Bay Check-In via 'I'm here' Link
- Given a customer opens their unique 'I'm here' link for an active order, when the page loads successfully, then the system records a single arrival event with timestamp within 1 second and associates it with the correct order and store. - Given an arrival event is recorded, when a bay is already reserved for that order, then the bay status changes to Occupied within 2 seconds and the order is linked to that bay. - Given multiple clicks of the 'I'm here' link, when duplicate arrival events are received within 5 minutes, then the system deduplicates them and updates the original event timestamp without creating extra bay occupancy records. - Given an arrival event is recorded, when staff view the Expo/Captain timeline, then the order shows Arrived state and current bay (if any) within 2 seconds of the event.
Forecasted Bay Free Times Align Fire/Hold
- Given a bay becomes Occupied, when forecasting is executed, then the projected free time is generated with 80th-percentile absolute error ≤ 2 minutes over a rolling 2-hour window. - Given an order is within 15 minutes of its fire time, when projected bay availability changes by ≥ 2 minutes, then the Fire Curve adjusts the order’s fire/hold timestamps accordingly and flags the change in the Expo view within 2 seconds. - Given an order has an updated readiness time, when the new time is earlier than the projected bay free time, then the system targets readiness to be no more than 3 minutes before projected bay free time for 90% of orders.
Bay Reservation and Suggestion for Imminent Ready Orders
- Given an order’s readiness ETA is within 10 minutes and no bay is assigned, when a bay is forecast to free within ±5 minutes of readiness ETA, then the system reserves that bay and marks it Reserved with the order ID. - Given a bay is Reserved for an order, when a second order attempts to reserve the same time slot, then the system prevents double-booking and suggests the next-best bay with a conflict-free window. - Given a bay reservation is created or changed, when customer communication is enabled, then an SMS is sent/updated with the bay number and arrival instructions within 5 seconds. - Given staff override a system reservation, when the override is confirmed, then the previous reservation is released, occupancy and timelines are updated within 2 seconds, and the customer is notified of the new bay.
Mispark Detection and Zone Remapping
- Given an order is Reserved for Bay N, when the customer reports arrival in a different bay or zone, then the system detects the mismatch and surfaces a Remap action to staff within 2 seconds. - Given staff select Remap to Bay M, when the action is confirmed, then Bay N is freed, Bay M is marked Occupied by the order, and Fire Curve timelines and queue positions are updated within 2 seconds. - Given a remap occurs, when the customer needs updated guidance, then an SMS with the corrected bay/zone is sent within 5 seconds. - Given a remap occurs, when system logs are reviewed, then a single audit record exists capturing prior bay, new bay, user, timestamp, and reason.
Dynamic Replanning on Bay Turnover Deviations
- Given a bay remains occupied beyond its forecast by more than 3 minutes, when the threshold is exceeded, then the system recalculates impacted orders’ fire/hold times and highlights delays to Expo/Captain within 2 seconds. - Given a bay frees earlier than forecast by more than 3 minutes, when the event occurs, then the system advances the next suitable order’s readiness and optionally prompts an early-arrival SMS template. - Given frequent turnover fluctuations, when replanning is triggered, then any single order’s plan is rate-limited to at most one automatic change per 90 seconds. - Given replanning adjusts an order, when the change would create a double reservation or bay conflict, then the system selects an alternate bay or defers readiness to avoid conflict.
SMS and Bay Assignment Integration for Optimal Routing
- Given a bay is reserved or assigned, when integration events are published, then the existing Bay Assignment module receives a BayAssigned or BayChanged event with order ID, bay ID, status, and timestamps and acknowledges within 1 second. - Given an SMS instruction needs to be sent, when the BayAssigned/BayChanged event is emitted, then the SMS service receives a payload including bay number, zone, arrival link, and short map URL and sends within 5 seconds or returns a retryable error. - Given an integration or SMS send fails after 2 retries, when failure is confirmed, then the system flags the order for manual intervention and surfaces a one-tap “Read out bay” prompt in staff UI. - Given bay assignment changes from any source (system or staff), when the change is saved, then all connected UIs reflect the new assignment within 2 seconds and the prior bay is freed.
Captain Readiness Cues
"As a curbside captain, I want clear, timely cues on which orders to bag, stage, or run so that I can execute fast handoffs and keep bays turning."
Description

Provide captains with concise, device-agnostic readiness cues that are synced to slot load: what to bag, what to stage, and what to run now. Include low-latency visual/audio indicators, batching suggestions for multi-order runs, and a one-tap handoff confirmation that frees the bay and updates the plan. Optionally trigger customer nudges (e.g., "Pull to Bay 3" or "Arriving in 1 minute?") via SMS when readiness and bay assignments are aligned. Works in the existing browser UI with no apps or hardware.

Acceptance Criteria
Real-Time Readiness Cues Synced to Slot Load
Given a signed-in Captain viewing the Captain screen on any device, When the Fire Curve updates an order's readiness state, Then the Captain sees a cue labeled "Bag", "Stage", or "Run Now" with order ID, assigned bay (or "Bay Pending"), zone, and time-to-action. Given the backend publishes any change to readiness, bay assignment, or slot load, When the Captain screen is in focus, Then the cue list updates within 1.0 second at p95 and never later than 2.0 seconds. Given two cues have identical urgency, When they are displayed, Then they are sorted by earliest ETA, then by bay number. Given an order has "Stage" or "Run Now" without a bay, When a bay is assigned, Then the cue updates to the bay number within 1.0 second at p95. Given a cue is resolved by handoff, When the Captain revisits the view, Then the resolved cue no longer appears in the active list.
Low-Latency Visual and Audio Indicators
Given a new "Run Now" cue appears, When it is rendered, Then an audio chime plays within 300 ms at p95 and the cue visually highlights for 2 seconds. Given the Captain mutes sounds, When new cues appear, Then no sound plays and visual indicators still render; the mute state persists for the session. Given a screen reader is active, When a new cue appears, Then the cue text is announced via an ARIA live region without trapping focus. Given any cue is displayed, When checked for contrast, Then text and icons meet WCAG 2.1 AA contrast ratios. Given the device is in silent/do-not-disturb mode, When a sound would play, Then the application respects device settings and does not override silent mode.
Batching Suggestions for Multi-Order Runs
Given two or more orders have "Stage" or "Run Now" states targeting the same zone or adjacent bays within a 2-minute window, When viewed by a Captain, Then the system suggests a batch grouping with a recommended run sequence. Given a batch suggestion is displayed, When the Captain accepts it, Then the batched orders are tagged as a batch, shown as a single run card, and excluded from further suggestions. Given a batch suggestion is dismissed, When recalculated, Then the same batch is not re-suggested for 3 minutes unless membership or timing changes. Given order timing or bay assignment changes, When a suggestion is affected, Then the suggestion updates or retracts within 1.0 second at p95. Given a batch is accepted, When the Captain taps Handoff, Then all orders in the batch are marked handed off in a single action.
One-Tap Handoff Confirmation Frees Bay and Updates Plan
Given a Captain taps Handoff on an order or batch, When the request is sent, Then the server records the handoff, frees the associated bay(s), and updates the Fire Curve plan within 2.0 seconds at p95. Given a handoff is recorded, When other clients (e.g., Expo view, other Captains) are connected, Then they receive the update and see the freed bay within 2.0 seconds at p95. Given intermittent connectivity, When a handoff is tapped, Then the action is queued and visibly marked as Pending; the bay is not freed client-side until server confirmation is received. Given multiple taps or concurrent clients, When the same order is handed off more than once, Then only the first confirmed event is applied and subsequent attempts are idempotently ignored. Given audit requirements, When a handoff is confirmed, Then an audit record stores order ID, bay, timestamp (UTC), and user ID.
Optional SMS Nudges Aligned to Readiness and Bay Assignment
Given a customer has SMS opt-in and an assigned bay exists, When an order reaches "Stage" and arrival is not detected, Then the system sends "Pull to Bay {N}" once within 5 seconds at p95 of the trigger. Given readiness is "Stage" and no bay is yet assigned, When the ETA window opens, Then the system sends "Arriving in 1 minute?" with a quick-reply option and an "I'm here" link, at most once. Given any nudge has been sent, When additional triggers occur, Then total nudges per order do not exceed 2 and are spaced at least 3 minutes apart. Given the customer replies YES or taps the "I'm here" link, When processed, Then the order's arrival state updates within 2.0 seconds at p95 and Captain cues refresh accordingly. Given the customer replies STOP, When future triggers occur, Then no SMS nudges are sent and the order is flagged as opted out.
Device-Agnostic Browser UI Compatibility and Accessibility
Given the Captain view is opened on Safari, Chrome, Edge, or Firefox (current and previous major versions) on mobile or desktop, When the page loads, Then all cues, batching, indicators, and handoff actions function without requiring any app install or external hardware. Given viewport widths from 320 px to 1920 px, When the Captain view is resized, Then all cues remain readable and tappable with 44x44 px minimum touch targets and no horizontal scrolling. Given only a keyboard is available, When navigating the Captain view, Then all actionable elements are reachable via logical Tab order and operable via Enter/Space with visible focus states. Given degraded network conditions (400 ms RTT, 1.5 Mbps), When using the Captain view, Then interactive actions show optimistic UI with clear Pending/Error states and remain responsive. Given the page is refreshed within the same session, When returning to the Captain view, Then role and preferences (e.g., mute) are restored from session storage without reauthentication.
Dynamic Reflow & Exception Handling
"As an operator, I want Fire Curve to adapt when plans change so that food quality stays high and handoffs remain smooth during surges."
Description

Automatically replan fires, holds, and staging when reality diverges—early/late arrivals, kitchen delays, no-shows, remakes, or bay blockages. Respect prep minimums and hold tolerances, recommend partial refires when windows are exceeded, and escalate with targeted alerts to expo and captains. Provide safe manual overrides with an audit trail and guardrails to prevent impossible schedules. Generate metrics on remakes avoided, lateness risk, and average handoff latency to improve the Arrival Curve Engine over time.

Acceptance Criteria
Early Arrival Reflow Adjusts Fire/Hold/Stage
Given an order with item-level prep minimums and hold tolerances and a forecasted arrival time When the customer checks in 8 or more minutes earlier than the latest forecast OR their assigned bay frees up earlier Then the system recalculates fire/hold/stage within 5 seconds and updates the expo timeline And no item is scheduled earlier than its prep minimum relative to now And no item’s projected hold exceeds its tolerance at the new handoff time And the assigned bay capacity is not exceeded during the new handoff window And targeted in-app alerts are sent to expo and the relevant captain within 2 seconds of reflow And an audit entry records the reflow with before/after times, reason=EarlyArrival, and user/system actor
Late Arrival Exceeds Hold Tolerance Triggers Partial Refire
Given an order with at least one hot item whose hold tolerance is defined And the kitchen completed the order at time Tc When the customer arrival time Ta is later than Tc + the shortest item hold tolerance by 1 minute or more Then the system flags only the items exceeding tolerance as at-risk and recommends a partial refire plan And the recommendation shows per-item refire start time, finish time, and impact on handoff ETA And items still within tolerance are not included in the refire plan And a single-tap confirm from expo schedules the refire respecting prep minima and bay readiness And targeted alerts are sent to the captain with the new readiness cue And metrics are recorded: partial_refires_recommended +=1, items_refired = count, remakes_avoided += (total_items_in_order - items_refired) And an audit entry captures the recommendation, decision, actor, and timestamps
Station Delay Propagates Reflow Respecting Prep Minimums
Given a station delay event of +6 minutes is reported for a station producing items on an in-progress order When the delay is logged by a captain or detected by throughput signals Then the system recalculates fire times for dependent items within 5 seconds to maintain synchronized finish And no item is rescheduled earlier than now or earlier than its prep minimum And projected holds for all items remain within their tolerances; items that cannot be kept within tolerance are flagged with at-risk tags And if predicted handoff slip exceeds 4 minutes, targeted alerts are sent to expo and affected captains And the expo timeline and captain readiness cues update to the new plan And an audit entry records the delay, reflow results, affected items, and risk score
No-Show Escalation and Bay Reassignment
Given an order has an assigned pickup window and bay When no customer check-in occurs by 10 minutes past the planned arrival time Then the system marks the order status as No-Show and releases the bay immediately And any pending hot fires for that order are paused/cancelled to avoid waste, with a recommendation to cold-hold if applicable And the freed bay is reassigned to the next queued order within 3 seconds without exceeding bay capacity And targeted in-app alerts are sent to expo and captains indicating No-Show and new bay assignment And an audit entry records the status change, bay reassignment, and actor=system And if the customer later checks in, the system reflows fire/hold/stage within 5 seconds based on the new actual arrival
Bay Blocked Reassignment and Timeline Update
Given a captain marks Bay N as Blocked with an expected clear time When the blocked state is set Then the system immediately removes Bay N from the assignment pool and reassigns impacted orders to alternative bays within 3 seconds And the expo timeline and captain views update the handoff slots and walking paths accordingly And no reassignment creates overlapping handoffs beyond the capacity of the destination bays And targeted alerts are sent to expo and captains indicating bay blockage and new assignments And when Bay N is unblocked, the system reintroduces it into the pool and reflows without disrupting already confirmed handoffs And all changes are logged in the audit trail with before/after bay, reason=BayBlocked/Unblocked, and actor
Manual Override With Guardrails and Audit Trail
Given an expo attempts to manually set fire/hold/stage times or change bay assignments When the override violates prep minimums, item hold tolerances, or bay capacity Then the system blocks the change and displays a specific validation message indicating the violated rule And when the override is within constraints, the system applies it and reflows dependent items within 5 seconds And every accepted override requires a reason and records user, timestamp, before/after values, affected orders/items, and resulting risk score in the audit trail And an Undo option is available for 5 minutes after the change to revert to the prior plan in one action And overrides cannot schedule events in the past or create negative prep durations And all guardrail rejections and successful overrides are visible in an audit feed filterable by order, user, and time
Metrics Capture and Lateness Risk Scoring
Given orders progress through normal and exception flows (reflows, refires, no-shows, bay blockages) When the shift ends or data is queried for a time range Then the system computes and stores: average handoff latency (actual handoff minus planned), lateness_risk_alert_lead_time (minutes between first risk alert and actual handoff), remakes_avoided (items), and count of bay conflicts avoided And metrics are attributable by order, item, station, captain, and time bucket (15-minute intervals) And a dashboard exposes these metrics with filters (date, zone, station, captain) and export to CSV/JSON And the Arrival Curve Engine receives labeled examples (features + outcomes) within 60 seconds of order close via a documented schema, with PII masked and order IDs pseudonymized And data quality checks ensure <1% missing critical fields per day; failures raise an alert to admins And all metric computations and exports are logged in the audit trail with job IDs and timestamps

Rush Replay

Generates a post‑event breakdown—slot fill %, overflow triggers, dwell by zone, and saved minutes from diversions—plus one‑tap recommendations for next time. Multi‑unit ops can compare locations and clone winning templates. Continuous learning turns every surge into a smoother one.

Requirements

Event Data Capture & Normalization
"As an operations manager, I want rush-related events captured accurately and consistently so that Rush Replay insights are trustworthy and comparable over time."
Description

Implement a reliable, low-latency pipeline that captures and normalizes all rush-relevant CurbPing signals—order creation/ready times, SMS “I’m here” pings, bay assignments, overflow triggers, diversions, dwell by zone, and handoff completion—into a consistent, queryable schema. Ensure idempotent ingestion from existing SMS-first flows and browser links without requiring apps or new hardware. Tag events with location, staff shift, and daypart; support backfill and late-arriving data. Enforce data quality rules (required fields, timestamp sanity, location integrity), PII minimization, and retention policies aligned with privacy standards. Provide a versioned data contract for downstream consumers (metrics engine, recommendations) to guarantee stability and enable schema evolution.

Acceptance Criteria
Idempotent Ingestion Across SMS and Browser Sources
Given two identical events with the same deduplication keys (order_id, event_type, source, occurred_at), When both are ingested within a 10-minute window, Then exactly one normalized record is persisted and exposed to consumers. Given a transient retry causes the same browser "I'm here" ping to be sent three times, When processed, Then the deduplication metric increments and no duplicate downstream notifications are emitted. Given two events differ in payload hash or occurred_at by more than 1 second, When ingested, Then both are persisted as distinct records. Given an event missing any deduplication key, When received, Then ingestion rejects it with validation_error=missing_dedup_keys and the event is not persisted.
End-to-End Low-Latency Event Availability
Given steady-state load of 200 events per minute across 10 locations, When events are ingested, Then p95 time from receipt to queryable in the normalized store is <= 3 seconds and p99 <= 10 seconds. Given a burst of 3x steady-state for 5 consecutive minutes, When ingested, Then p95 latency remains <= 5 seconds and no data loss occurs. Given latency SLO breaches for 3 consecutive minutes, When detected, Then an alert is emitted to the on-call channel.
Data Quality Validation and Integrity Enforcement
Given any incoming event, When validated, Then required fields (event_id, event_type, occurred_at, received_at, location_id, source, schema_version) are present or the event is rejected with a specific error code and reason. Given occurred_at is >5 minutes in the future or >7 days in the past relative to received_at, When validated, Then the event is quarantined with reason=timestamp_out_of_range and excluded from consumer queries. Given a location_id that does not exist in the location registry, When validated, Then the event is rejected with reason=unknown_location. Given weekly processing, When measured, Then >= 99.5% of events pass all validations; <= 0.3% are quarantined; <= 0.2% are rejected; metrics are published to monitoring.
Versioned Data Contract with Backward Compatibility
Given schema_version uses semantic versioning, When a minor version is released, Then it adds only optional fields and all existing consumers pass contract tests without change. Given a breaking change is required, When a major version is released, Then v(N-1) remains supported in production for at least 90 days with dual-write and dual-read. Given any persisted event, When inspected, Then it includes schema_version and producer_name conforming to the published JSON Schema. Given contract documentation, When published, Then JSON Schema, example payloads, and a dated changelog are available to consumers.
Accurate Location, Shift, and Daypart Tagging
Given an event with occurred_at and location_id, When enriched, Then shift_id and daypart are populated according to the location’s schedule and daypart rules. Given overlapping shifts, When enrichment runs, Then shift_id selection prefers the shift whose window contains occurred_at; ties resolve by earliest shift start. Given no active shift is found, When enrichment runs, Then shift_id is null and resolution_status=enrichment_missing_shift is set. Given a labeled golden dataset of 1,000 events, When evaluated, Then >= 99% have correct shift_id and >= 99% have correct daypart.
Backfill and Late-Arriving Event Handling
Given a backfill request specifying location_id and date range, When executed, Then all eligible events are ingested with original occurred_at preserved and no duplicates are created. Given late-arriving events up to 48 hours after occurred_at, When received, Then they are accepted and materialized views/aggregates are updated within 5 minutes of ingestion. Given realtime ingestion concurrent with backfill, When measured, Then realtime p95 latency does not degrade by more than 10% compared to baseline. Given a backfill job completes, When audited, Then an immutable log entry records actor, time, parameters, and counts of inserted, deduped, rejected.
PII Minimization and Retention Compliance
Given incoming payloads contain phone numbers, When normalized, Then no raw phone numbers are persisted; a salted one-way hash is stored instead and the salt rotates at least every 30 days. Given normalized events contain any PII surrogate fields, When nightly retention runs, Then all PII surrogates older than 30 days are deleted; non-PII normalized events are retained for 180 days by default and then hard-deleted. Given a tenant-specific retention override, When configured, Then the override is applied within 24 hours and reflected in audit logs. Given a location offboards, When deprovisioned, Then all associated normalized and raw events are hard-deleted within 7 days and the deletion job result is recorded.
Rush Window Detection & Metrics Engine
"As a store manager, I want Rush Replay to auto-calculate key rush metrics so that I can understand performance without manual analysis."
Description

Automatically detect rush windows using observed order volume, arrival density, and queue pressure, or allow manual selection of a time range. Compute core post-event KPIs: slot fill percentage over time, overflow trigger counts and timing, dwell time by zone (bays, curb, overflow), and minutes saved via diversions versus a baseline. Implement a transparent baseline model (e.g., historical medians by daypart/weather) and counterfactual logic to estimate saved minutes. Handle data gaps and clock drift; provide confidence intervals and data-quality flags per metric. Deliver metrics within minutes after a rush ends and expose them via a stable service API for UI and export.

Acceptance Criteria
Auto-Detect Rush Window From Live Signals
Given live order, arrival, and queue events are ingested in real time and ground-truth rush periods exist in a test dataset When signals exceed adaptive thresholds for at least 3 consecutive minutes Then the engine marks the rush start no later than 2 minutes after the third consecutive minute and tags contributing signals Given signals fall below thresholds for 5 consecutive minutes When stabilization persists Then the engine marks the rush end within 3 minutes and closes the window Given a short spike below 3 minutes When evaluated Then no rush window is opened Given input gaps up to 2 minutes or clock drift up to ±60 seconds are detected When processing Then the engine imputes per documented rules and adds data-quality flags to the window Given an API request for detected windows for a location and day When called Then the response contains ordered windows with start/end timestamps (ISO-8601 with timezone), signal summary, and a confidence score
Manual Rush Window Selection and Override
Given a valid manual rush time range (start < end, timezone specified) submitted via API with an idempotency key When processed Then a manual window is created/updated with a unique id and supersedes overlapping auto-detected windows Given overlapping manual windows are submitted When processed Then overlaps are rejected with HTTP 409 and a clear message, and no duplicates are created Given an invalid time range, missing timezone, or range > 6 hours When submitted Then the API returns HTTP 400 with field-level validation errors and no window is created Given metrics are requested for a manual window When computed Then all KPIs are generated using the same methods as auto-detected windows and responses include source=manual
Slot Fill Percentage Time Series
Given configured bay capacity by location and time (including temporary closures) and bay assignment events When computing slot fill at 1-minute granularity for a rush window Then the engine outputs a time series utilization = occupied_bays/capacity with values in [0,1], rounded to 2 decimals, and a 95% CI per minute Given capacity is missing for any minute When encountered Then that minute is flagged capacity_unknown, excluded from aggregates, and the window includes a data-quality flag with affected minute count Given API consumers request slot fill series When called Then the response returns ordered points with timestamp, capacity, occupied count, CI, and flags, completing within 1 second p95
Overflow Trigger Counts and Timing
Given overflow rules (e.g., utilization >= 1.0 for >= 2 consecutive minutes or queue length >= threshold) When evaluating a rush window Then the engine returns total trigger count, first/last trigger timestamps, and an ordered list of trigger events with timing CI (±1 minute) Given triggers occur within a cooldown period of 5 minutes When deduplicating Then only the first trigger in the cooldown is counted and subsequent ones are marked deduped with reason Given no trigger condition is met When processed Then the count is 0 and timing fields are null while the response still includes rules_used and flags
Dwell Time by Zone with Confidence and Flags
Given arrival and departure events mapped to zones {bays, curb, overflow} with possible missing timestamps or drift When computing dwell for a rush window Then the engine outputs per-zone metrics: sample_size, mean, median, p90, p95, max, and 95% CI for mean and median, plus excluded_count Given records with only one timestamp or drift > 90 seconds between sources When encountered Then those records are excluded from primary stats, counted in excluded_count, and the window is flagged time_incomplete or clock_drift_high Given zone mapping changes mid-rush When observed Then metrics reflect the mapping effective at each event time and the response includes mapping_version
Counterfactual Minutes Saved with Transparent Baseline
Given diversions recommended and accepted during a rush and a baseline using historical medians by daypart and weather with ≥14 prior matching days (fallback to all-time median if insufficient) When estimating counterfactual dwell per order Then saved_minutes = max(0, baseline_dwell − observed_dwell) is computed per order and aggregated (total, average) with 95% CI and sample size Given insufficient history (<7 matching days) or out-of-domain weather When detected Then the engine applies a documented fallback, labels baseline_fallback with reason, and widens CI accordingly Given weekly backtesting on a holdout set When evaluated Then baseline MAPE for dwell in non-rush periods is ≤ 10% and the backtest report is retrievable via API with model_version and as_of date
Post-Rush Processing Latency and Stable API
Given a rush window ends When the last qualifying minute elapses Then all KPIs are computed and available via the service API within 5 minutes p95 and 10 minutes p99, with up to 3 automatic retries on failure Given a client requests metrics for a window When calling the API Then responses conform to a versioned, stable schema (v1), include metric-level confidence intervals and data-quality flags, and return HTTP 200 within 800 ms p95 Given partial data source outages occur When serving requests Then the API responds with HTTP 206 Partial Content including impacted metrics, flags, and retry-after guidance; monthly uptime is ≥ 99.9%
Rush Replay Dashboard & Export
"As a shift lead, I want a clear post-rush dashboard and easy exports so that I can review what happened and share findings with my team."
Description

Provide an interactive, mobile-first dashboard that visualizes each rush: time-series slot utilization, overflow trigger timeline, zone dwell distribution, and a heatmap of bay usage. Surface top insights (e.g., peak congestion minute, primary bottleneck) and allow filtering by daypart, weather tag, and order channel. Enable one-tap sharing and export to CSV and PDF with branded formatting. Integrate with existing CurbPing roles/permissions so managers can see their locations while staff see only local rushes. Ensure loads under 2 seconds for typical rush datasets and accessibility compliance (keyboard nav, contrast, alt text).

Acceptance Criteria
Core Visualizations Render Correctly
Given a completed rush dataset with minute-level utilization, overflow triggers, zone dwell events, and bay usage totals When the dashboard loads on a mobile device 375–430 px wide Then the time-series slot utilization chart displays minute-by-minute values that exactly match backend data for all minutes, supports horizontal pan, and shows a tooltip with minute, slots used, and slots available Given recorded overflow triggers with timestamps and types When the overflow timeline is rendered Then each trigger appears at the correct timestamp with the correct label and count, and tapping expands to show cause metadata Given zone dwell events When the dwell distribution is rendered Then the chart displays per-zone counts and p50/p90/p95 dwell times that match backend aggregates within ±1 second, and zones can be toggled on/off Given bay usage totals When the bay heatmap is rendered Then each bay cell intensity corresponds to usage counts with a visible legend scale, and tapping a bay reveals total uses and peak 5-minute window
Top Insights Calculation & Display
Given a rush dataset has been processed When insights are computed Then the dashboard surfaces at least: peak congestion minute, primary bottleneck zone, highest 5-minute overflow trigger rate, and minutes saved from diversions, each with numeric values and context Given multiple minutes share the same peak utilization When the peak congestion minute is determined Then the earliest minute in the tie is displayed and a tie indicator is shown Given an insight chip is displayed When a user taps the chip Then the related visualization scrolls into view and highlights the corresponding time/zone for 3 seconds
Filtering by Daypart, Weather Tag, and Order Channel
Given dayparts (Breakfast, Lunch, Dinner) and a custom time range, weather tags, and order channel tags exist When the user applies any combination of these filters Then all visualizations and insights update using AND logic without a full page reload, and an Active Filters bar lists all applied filters Given filters are applied When the page URL is copied or shared Then the filter state is encoded in query parameters and restored exactly on load Given the user selects Reset Filters When the reset action is confirmed Then all filters revert to defaults and the unfiltered results are displayed
Export to CSV and PDF with Branding and One-Tap Sharing
Given a rush is in view When the user taps Export CSV Then a download starts that contains at minimum the time-series utilization, overflow triggers, zone dwell summary, and bay usage datasets in CSV format, with headers and ISO-8601 timestamps Given a rush is in view When the user taps Export PDF Then a branded PDF (logo, location name, date/time range) is generated containing the four visualizations and top insights, with consistent fonts, legend keys, and page numbers Given the user taps Share When the share action completes Then a permission-respecting link to the current filtered view is generated and exposed via the device share sheet and copied to clipboard Given an export completes When the files are saved Then file names follow the pattern RushReplay_{location}_{YYYY-MM-DD}_{rushId}.{csv|pdf} and times reflect the location time zone
Role-Based Access Control and Data Scoping
Given a user has the Manager role for specific locations When accessing the Rush Replay dashboard Then they can view and filter rushes only for their assigned locations; attempts to access other locations return a 403 with a friendly error message Given a user has the Staff role When accessing the Rush Replay dashboard Then only local rushes for their current location are visible; cross-location comparison and cloning features are hidden Given a user generates a share link When a recipient opens the link Then access is granted only if the recipient’s role permits viewing that rush; otherwise a 403 is returned and no rush metadata is leaked
Performance: Typical Rush Loads Under 2 Seconds
Given a typical rush dataset (≤500 orders, ≤10 bays, ≤5 zones, 120-minute window) When the dashboard is loaded on a mid-tier mobile device over simulated 4G (≈1.6 Mbps, 300 ms RTT) Then time-to-interactive is ≤2.0 seconds at p95 across 30 cold loads, and total transferred payload for the initial view is ≤1.0 MB Given filters are applied When results are re-rendered Then visible charts update within 300 ms after data retrieval completes and maintain smooth scrolling and panning (no dropped frames perceptible)
Accessibility WCAG 2.1 AA Compliance
Given a keyboard-only user When navigating the dashboard and interacting with charts and controls Then all interactive elements are reachable in a logical order, operable via keyboard, and show a visible focus indicator Given a screen reader user When reading the dashboard Then each visualization has an accessible name and description (aria-label/aria-describedby) summarizing key metrics, and an equivalent data table is available for charts Given color-dependent visuals When charts and UI render Then contrast ratios meet or exceed 4.5:1, information is not conveyed by color alone, and a high-contrast mode maintains readability Given a PDF export is generated When a user opens the PDF in an assistive technology Then it is a tagged PDF with correct reading order, labeled figures, and alt text for charts
One‑Tap Operational Recommendations
"As an operator, I want one-tap recommendations with estimated impact so that I can quickly apply improvements for the next rush."
Description

Generate actionable, context-aware recommendations after each rush—e.g., adjust bay capacity by time block, tweak SMS auto-text timing, set overflow trigger thresholds, or add staging staff—each with estimated impact (minutes saved, reduced dwell) and implementation risk. Present recommendations inline on the Rush Replay dashboard with a single-tap “Apply to template” action that updates the location’s operational template. Include guardrails: preview diffs, required permissions, rollback, and change logs. Learn from acceptance/decline to refine future suggestions.

Acceptance Criteria
Inline Recommendations Post‑Rush
Given a location has completed a rush with Rush Replay metrics available When a user with view access opens the Rush Replay dashboard for that rush Then the system displays 3–10 context-aware recommendation cards within 2 seconds of dashboard load And each card shows: action title, rationale referencing at least one rush metric, estimated impact in minutes saved and/or dwell reduction percentage with a 90% confidence range, and an implementation risk level (Low/Medium/High) And each card includes a single "Apply to template" control that is enabled or disabled based on the user's permissions And recommendation cards are sorted by descending estimated minutes saved; ties are broken by lower risk first
One‑Tap Apply with Diff Preview and Permission Gate
Given a user selects "Apply to template" on a recommendation When the apply flow opens Then a modal presents a read-only diff of template fields to be changed, including before and after values for only the impacted keys And the modal displays the planned activation scope (immediate or next rush), and any prerequisite checks And if the user lacks Template:Edit permission for the location, the Confirm Apply button is disabled and an inline message explains the required permission And if the user has permission and taps Confirm Apply, the system atomically writes the changes, increments the template version, and shows a success toast within 1 second And if validation fails, no changes are written and field-level error messages identify the failing rules
Rollback to Previous Template Version
Given a recommendation change set was applied to a location template When a user opens the change log entry and selects Rollback Then the system creates a new template version that reverts only the fields changed by that entry while preserving unrelated subsequent edits And the template state matches the pre-apply values for those fields And a success toast is shown and the Rush Replay dashboard reflects the reverted values on refresh And the rollback action is recorded in the change log with a link back to the original apply event
Change Log and Audit Trail Recording
Given any Apply or Rollback action completes When an authorized user views the location’s change log Then each entry shows: timestamp (UTC), actor (user id and display name), action type (Apply or Rollback), source rush id, affected template keys with old and new values, estimated impact and risk at time of apply, and resulting version number And entries are filterable by action type, actor, date range, and key changed And users without Audit:View permission cannot access the change log
Learning From Acceptance/Decline Signals
Given a user accepts or declines a recommendation and optionally selects a reason tag When the next recommendation set is generated for a rush with matching context tags (e.g., same daypart and similar volume) Then accepted recommendation types are boosted by at least one rank position relative to similarly scored items And recommendations declined with reason Not Applicable are suppressed for 30 days for the same location and context And recommendations declined with reason Too Risky are shown with a badge and are ranked at least two positions lower than before, if present And an event is stored capturing decision, user id, timestamp, recommendation type, context tags, and prior rank
Validation Guardrails and Conflict Resolution
Given a recommendation would create conflicting settings (e.g., bay capacity > physical bays or overlapping SMS auto-text timings) When the user attempts to apply the recommendation Then the system blocks the apply and lists each conflict with the specific field, current value, proposed value, and violated rule And the modal offers auto-resolve options (e.g., cap at max bays, shift SMS offset) that can be selected individually And selecting an auto-resolve updates the diff preview and re-runs validation in under 500 ms And the Confirm Apply button remains disabled until all conflicts are resolved or removed
Multi‑Unit Comparison & Benchmarking
"As a regional manager, I want to compare rush performance across my locations so that I can identify outliers and spread best practices."
Description

Enable regional and brand-level views that compare locations across identical or similar rush windows by daypart and conditions. Provide sortable tables and small multiples for slot fill %, overflow triggers, dwell by zone, and minutes saved from diversions, with percentile ranks and peer averages. Allow cohort filtering (store format, parking layout, staffing level) and highlight top/bottom performers. Respect RBAC so users only see authorized locations. Support drill-down from aggregate to specific rush details.

Acceptance Criteria
Regional/Brand Daypart Comparison
Given I am an authorized brand or regional user and select Brand, Region, Daypart, Date Range, and Conditions When I run Compare Then the table lists only locations I am authorized to view that had rush windows matching the selected daypart and conditions within the date range And each location row includes Slot Fill %, Overflow Triggers (count), Avg Dwell by Zone (A/B/C), and Minutes Saved from Diversions And locations with no qualifying data display "No data" and are excluded from aggregate statistics And the compare view loads within 2 seconds for up to 300 locations
Sortable Tables & Small Multiples
Given the comparison table is displayed for N<=300 locations When I click a column header for any metric (e.g., Slot Fill %) Then rows sort by that metric descending; clicking again toggles ascending; a sort indicator appears And sorting completes within 300 ms for N<=300 When I hover a small-multiples chart thumbnail for a location Then the metric sparkline renders with tooltips showing timestamp and value And each location displays percentile rank (0-100) and cohort peer average for each metric rounded to 1 decimal And small multiples render within 3 seconds total
Cohort Filters (Format, Layout, Staffing)
Given cohort filters for Store Format, Parking Layout, and Staffing Level are visible and support multi-select When I select any combination of filter values Then the table, charts, percentile ranks, and peer averages refresh to reflect only the filtered cohort And a pill summary shows active filters and counts And the cohort definition is encoded in the URL so the view can be shared and reloaded with identical results And refresh completes within 2 seconds for N<=300
Top/Bottom Performers Highlighting
Given a cohort with at least 20 locations and a selected primary sort metric When the comparison table renders Then top 10% and bottom 10% performers by the primary sort metric are highlighted with distinct, WCAG AA-compliant badges and a legend And ties at the percentile boundary are included in the highlight group And highlights update immediately when the sort metric or cohort changes
RBAC Visibility Enforcement
Given my role is Brand Admin, Regional Manager, or Store Manager with specific location assignments When I open Multi-Unit Comparison Then I can only view locations authorized by my role and assignments And unauthorized locations are omitted from tables and charts, and aggregates compute only from authorized locations And attempts to access disallowed location IDs via URL parameters return 403 and are logged with user id, timestamp, and requested resource
Drill-Down to Rush Details
Given the comparison table is filtered to a cohort and date range When I click a location's metric value or small-multiples thumbnail Then I am taken to that location's Rush Replay detail for the corresponding rush window with filters and date context preserved And a breadcrumb allows navigation back to the comparison view without losing state And the detail view loads within 3 seconds and includes slot fill timeline, overflow trigger events, dwell by zone breakdown, and diversion minutes
Percentiles and Peer Averages Accuracy
Given a cohort selection, metric, and date range When percentile ranks and peer averages are displayed Then the percentile for each location equals its rank among the cohort using the nearest-rank method within +/-0.5 percentile And the peer average equals the arithmetic mean across the cohort excluding "No data" locations and matches backend reference within +/-0.1 units And results update within 1 second when the cohort changes
Template Cloning & Rollout
"As a multi-unit operator, I want to clone and schedule proven templates across stores so that improvements scale quickly and safely."
Description

Allow users to clone a “winning” operational template (bay counts by block, slot durations, overflow rules, auto-text timings) from one location/rush and propagate it to one or many locations with scheduling by daypart. Provide validation against local constraints (bay count, hours) and simulate expected impact using recent data. Include bulk-apply, staged rollout, and automatic rollback on negative impact signals. Maintain version history with audit trails and restore points.

Acceptance Criteria
Clone Template Fidelity
Given a source location rush template with defined bay_counts_by_block, slot_durations, overflow_rules, and auto_text_timings When the user selects “Clone Template” from that source Then a new template is created with identical values for bay_counts_by_block, slot_durations, overflow_rules, and auto_text_timings And the new template gets a new template_id and version_id while retaining a reference to source_template_id and source_timestamp And a comparison view between source and clone shows zero differences across all configurable fields And the clone operation completes in ≤3 seconds under normal load And if the source template is missing or corrupted, the system returns error CP-TCL-404 and does not create a clone
Multi-Location Daypart Propagation
Given the user selects 1–100 target locations and assigns daypart schedules (e.g., Breakfast, Lunch, Dinner with local time ranges) When the user chooses “Apply Template” Then a pre-apply summary lists target locations with eligibility status and any conflicts detected And the system schedules activation in each location’s local timezone for the specified dayparts And, at the scheduled time, the template becomes Active at each eligible location within 5 minutes And the result report shows per-location status: Applied, Skipped (with reason), or Failed (with reason) And failed applications are retried automatically up to 2 times before final failure is reported
Local Constraint Validation Gate
Given a target location has configured max_bay_count and business_hours When the user runs “Validate Against Local Constraints” for a template Then the system flags conflicts where requested bay allocation exceeds max_bay_count or scheduled dayparts fall outside business_hours with rule IDs and clear messages And application is blocked until conflicts are resolved or user opts into Auto-Adjust And Auto-Adjust reduces bay allocations to max available and clips dayparts to open hours, previewing changes before confirmation And validation completes in ≤2 seconds per location
Impact Simulation Using Recent Data
Given the last 14 days of arrival, dwell, and overflow event data exist for the target location When the user runs “Simulate Impact” Then the system outputs predicted slot_fill_percent, overflow_trigger_count, average_dwell_by_zone, and minutes_saved_vs_baseline with 95% confidence intervals And a delta summary compares predictions to the prior 14-day baseline And if fewer than 200 orders exist in the lookback, the system returns CP-SIM-INSUFFICIENT with guidance to widen the window And the simulation completes in ≤30 seconds and caches results for 24 hours unless inputs change
Staged Rollout with Auto-Monitoring and Rollback
Given the user configures a staged rollout (e.g., Pilot 10% of bays for 2 rushes → 100%) with monitoring thresholds When the rollout starts Then the system monitors KPIs per rush: average_wait_time, overflow_triggers_per_rush, and customer_SMS_complaint_rate And if any KPI degrades by ≥10% vs baseline for two consecutive rushes or overflow_triggers increase by ≥20%, the system auto-rolls back to the previous template within 2 minutes And rollback notifies designated channels (email/SMS/web) and records an audit event with cause and metrics And if no negative signals occur during the pilot, the system auto-advances to the next stage on schedule
Bulk Apply with Versioning and Restore Points
Given the user bulk applies a template to 2 or more locations When the user confirms “Commit Rollout” Then the system writes a new version per location capturing author, timestamp, change_diff_summary, and restore_point_id And a global audit log records per-location outcome with status and any validation notes And the user can restore any location to a prior version, which completes within 2 minutes and restores all configurable fields And version history retains at least the last 100 versions per location and is immutable
Permissions and Audit for Cloning & Rollout
Given the organization enforces role-based access control When a user without Ops Admin or Location Manager (with assigned locations) attempts to clone, propagate, or restore Then the action is blocked with HTTP 403 and an audit entry is recorded And permitted users can perform the actions successfully And all actions (clone, validate, simulate, apply, rollback, restore) are audit-logged with user_id, role, IP, timestamp, affected_locations, and before_after_version_ids And audit logs are filterable by date range, location, user, and action, and exportable to CSV
Continuous Learning & Outcome Tracking
"As a multi-unit operator, I want the system to learn from what works at my stores so that each surge becomes smoother over time."
Description

Track the outcomes of applied recommendations and template changes across subsequent rushes to measure realized impact versus estimates. Update baselines and model weights for future savings estimates, detect drift (seasonality, staffing, construction), and trigger re-calibration when needed. Support lightweight A/B testing of operational settings where feasible and log acceptance/override behavior to improve recommendation relevance. Provide a privacy-preserving feedback loop that requires no additional hardware and works with CurbPing’s SMS-first flows.

Acceptance Criteria
Post-Rush Impact Attribution and Reporting
Given a rush where at least one recommendation or template change was applied When the rush ends Then the system computes realized impact versus pre-rush estimates for: wait time reduction (seconds), staff minutes saved, slot fill %, overflow trigger count, dwell time by zone (seconds), and diversions saved And the Rush Replay report including these realized vs. estimated metrics is available within 10 minutes of rush end And the rush is tagged with the recommendation and template version identifiers used And the report is persisted for at least 180 days and can be exported as CSV And multi-unit users can filter and compare these metrics across locations for the same date range
Automated Baseline and Model Weight Updates
Given a completed rush with ≥ 30 curbside orders and no data-quality flags When the Rush Replay is finalized Then baseline performance metrics are updated using a documented, versioned method and the prior baseline is retained for audit And savings-estimate model weights are updated and recorded with version ID, timestamp, training sample size, and feature set hash And a rolling 10-rush MAPE for savings estimates is computed and stored for the new and prior versions And if the new model’s 10-rush MAPE ≤ 20%, it is promoted to active; otherwise the system auto-rolls back and raises an alert
Drift Detection and Auto Re-calibration
Given monitored key metrics and estimate errors over recent rushes When absolute deviation from baseline exceeds 15% for 3 consecutive rushes OR an operator flags a known event (seasonality, staffing, construction) Then the system flags drift, schedules a recalibration job within 24 hours, pauses model promotions, and notifies operators via dashboard and email/SMS And the recalibrated model is only promoted if holdout validation shows ≥ 10% improvement in savings-estimate MAPE vs. pre-drift; otherwise the current model remains active and a “recalibration aborted” event is logged
Lightweight A/B Testing of Operational Settings
Given an operator configures an experiment on a single operational setting (e.g., bay assignment rule or SMS timing) When the experiment starts with target sample size and guardrails defined Then traffic is randomized at the order level with a default cap of 20% in variant unless explicitly increased And guardrails auto-stop the variant if median wait increases > 10% within a rush or overflow triggers increase by > 2 absolute vs control And a post-rush summary with effect size, confidence interval, p-value, and sample sizes is generated within 10 minutes And the experiment runs entirely within SMS flows and requires no additional hardware
Acceptance/Override Logging and Utilization
Given a recommendation is displayed to staff in live ops or Rush Replay When staff accepts or overrides it Then the system records action type, timestamp, user/location, and a selected reason code from a predefined list within two taps And logging coverage is ≥ 95% of recommendation events per rush And override/accept patterns are incorporated as features in the next model update and are traceable to a model version And subsequent recommendations surface a “why this” explanation referencing learned patterns when applicable
Privacy-Preserving, SMS-Only Feedback Loop
Given curbside operations using CurbPing without dedicated apps or hardware beacons When feedback and learning events are captured Then no GPS coordinates, license plates, or camera data are collected And phone numbers are stored as salted hashes and never exposed in analytics exports And raw event data retention is configurable (default 90 days); only aggregated metrics are retained beyond this window And all interactions remain within SMS-first flows; no additional hardware is required to enable continuous learning features

BrandBands

Distinct color bands and icon badges for each virtual brand and delivery app on a single arrivals board. One‑tap filters and quick toggles let dispatchers isolate a brand or app view instantly, preventing misroutes and missed pings while keeping the hallway focused and calm.

Requirements

Brand/App Color Band System
"As a dispatcher, I want each brand and delivery app to have a distinct, readable color band so that I can instantly identify and route orders without misreading or misrouting."
Description

Implement a theming system that assigns distinct, persistent color bands to each virtual brand and delivery app on the arrivals board. Provide tenant-level palette management with collision avoidance, WCAG AA contrast checks against light/dark themes, and automatic reassignment when conflicts arise. Ensure bands render consistently across list, tile, and compact board layouts without layout shift. Persist assignments across sessions and devices, support default templates, and allow per-location overrides so multi-unit operators can standardize or localize as needed. Integrate with existing order models so color bands apply as orders flow from POS and SMS “I’m here” links, and expose a safe fallback color when a brand/app has no mapping.

Acceptance Criteria
Unique Color Assignment and Persistence per Tenant
Given a tenant with active virtual brands and delivery apps on the arrivals board When the board renders or updates Then each brand/app is displayed with a distinct color band unique within that tenant's active set And the assigned color for a given brand/app ID is stable and identical across sessions and devices for that tenant/location until explicitly changed by an authorized user And the same assigned color is used consistently across list, tile, and compact layouts
WCAG AA Contrast Compliance in Light and Dark Themes
Given both light and dark board themes are available When color bands render with their foreground text and iconography Then the contrast ratio between band background and foreground text/icons is >= 4.5:1 And the contrast ratio between the band background and adjacent board background is >= 3:1 And any band that would fail these thresholds is automatically replaced by a compliant palette variant without user action
Collision Detection and Deterministic Auto-Reassignment
Given a new brand/app is added or a palette is changed for a tenant And the computed band color collides with an existing active assignment (same color value) When the collision is detected Then the system selects the next available non-conflicting color from the tenant palette or generates a compliant variant And only the incoming or last-modified item is reassigned; existing assignments remain unchanged And the reassignment completes within 1 second of detection and is deterministic given the same inputs And an audit log entry is created capturing actor/system, timestamp, and before/after colors
No Layout Shift Across Board Layouts
Given the arrivals board is viewed in list, tile, and compact layouts on supported browsers (latest Chrome, Safari, Firefox) When color bands are applied or updated Then element dimensions (height, width, padding) remain unchanged before vs after band application in all layouts And no cumulative layout shift (CLS) greater than 0.01 occurs during band rendering And scrollbars do not appear or disappear as a result of band rendering
Palette Management, Templates, and Per-Location Overrides
Given a tenant admin manages palettes When creating or editing a tenant-level palette Then duplicate color entries are prevented and the palette cannot be saved with duplicates And saving validates WCAG AA contrast for intended foregrounds in both light and dark themes When a default template palette is selected at the tenant level Then newly created locations inherit that template automatically When a location-level override palette is configured Then only that location uses the override without affecting other locations And when the tenant template is updated, locations without overrides update within 5 minutes; locations with overrides remain unchanged And an authorized user can reset a location to inherit from the tenant template with a single action
Order Integration and Fallback Behavior
Given orders flow from POS and via SMS "I'm here" links containing brandId and/or channel/app metadata When the arrivals board displays new or updated orders Then the correct color band is applied to both order entries and arrival pings based on the mapping And updates appear within 2 seconds of order ingest or state change When an order references a brand/app with no existing mapping Then a safe fallback band color is applied that meets WCAG AA for the current theme And an admin notification or queue entry is created to prompt mapping configuration And once a mapping is created, any visible orders update their bands in real time without page reload
Icon Badge Library & Mapping
"As an expo lead, I want clear app and brand badges next to each order so that I can route runners to the correct pickup shelf without having to read fine print."
Description

Add a badge system that displays recognizable icons for each delivery app and per-brand logos on the arrivals board. Provide a curated, CDN-hosted SVG icon set for major apps (e.g., DoorDash, Uber Eats, Grubhub) with automatic mapping from order source metadata, plus tenant-uploaded brand icons with size/format validation and safe fallbacks (generated initials). Cache icons client-side with versioned URLs for cache-busting, and defer-load assets to avoid UI jank. Expose an admin UI to manage mappings and preview how icons and color bands appear together across themes.

Acceptance Criteria
Auto-Mapping Third-Party App Icons from Order Metadata
Given an order payload contains source.app ∈ {"DoorDash","Uber Eats","Grubhub"} and a valid mapping exists When the arrivals board row renders Then the corresponding CDN-hosted SVG icon is displayed within 1s on first load and within 200ms on subsequent cached loads And the icon URL uses the configured HTTPS CDN base and a versioned path And the icon renders at 24x24px without distortion and has an accessible label equal to the app name And if the mapping key is unknown Then a default app badge is shown and a warning event with the unknown key is logged
Tenant Brand Icon Upload and Validation
Given a Tenant Admin opens Brand Icon settings When they upload a file Then only SVG or PNG files ≤ 256KB and ≥ 128x128 are accepted; others are rejected with specific validation messages And on successful save the icon is optimized, stored, and a versioned URL is generated And when the arrivals board renders for that brand the uploaded icon displays at 24x24px with pixel-density aware rendering and alt text set to the brand name
Generated Initials Fallback for Missing Brand Icons
Given a brand has no uploaded icon When its orders appear on the arrivals board Then a deterministic initials badge is rendered using the brand’s name initials with a color derived from the brand ID And the text-to-background contrast meets WCAG AA (≥ 4.5:1) in both light and dark themes And the badge size is 24x24px and introduces no layout shift
Client-Side Icon Caching and Versioned Cache Busting
Given icons are served from the CDN with Cache-Control max-age ≥ 31536000 and immutable When the board requests an icon a second time Then the browser serves it from cache and network requests are not issued for the same version And given an icon’s version changes (URL contains a new version/hash) When the board reloads after an admin save Then the new icon is fetched and displayed within 60s of the save And cache hit/miss telemetry is recorded per icon
Deferred Icon Loading Without UI Jank
Given the arrivals board initial render When icon assets have not finished loading Then placeholders reserve 24x24px and cumulative layout shift attributable to icons equals 0 And icons decode asynchronously and load with low priority And offscreen icons are lazy-loaded and do not block interaction And First Input Delay during icon loading is ≤ 100ms on target devices
Admin UI: Mapping Management and Theme Preview
Given an Admin opens the Icon Mapping UI When selecting a delivery app icon from the curated library or uploading a brand icon Then they can map it to a brand/app and save the mapping successfully And a live preview displays the icon with its associated color band in light and dark themes at 100%/125%/150% scale And upon save the mapping propagates to the arrivals board within 60s without a manual refresh
One-Tap Filters and Quick Toggles
"As a dispatcher on a busy line, I want to quickly filter the board to a single brand or app so that I can focus on the next few handoffs without noise."
Description

Provide one-tap filters to isolate arrivals by virtual brand or delivery app on the shared board. Support multi-select, an “only” shortcut, clear-all, and keyboard shortcuts for desktop dispatchers. Include sticky filter behavior per device/user, an optional display-lock mode for hallway monitors to prevent accidental changes, and real-time count chips that update as orders arrive or complete. Ensure filters are performant on low-cost hardware and work seamlessly with touch and mouse input.

Acceptance Criteria
One-Tap Brand/App Isolation with Multi-Select
Given a mixed arrivals board, when a dispatcher taps a brand or app chip, then only orders matching that chip become visible within 300 ms and non-matching orders are hidden without reloading the page. Given multiple chips within the same group (brand or app) are selected, when filtering is applied, then items matching any selected chip in that group are shown (OR logic within group). Given at least one brand chip and at least one app chip are selected, when filtering is applied, then only items matching any selected brand AND any selected app are shown (AND logic across groups). Given active filters, when a new matching order arrives, then it appears in the list within 1 second; when a matching order completes, then it is removed within 1 second. Given active filters, when a user selects an additional chip, then the list updates to reflect the expanded selection within 300 ms without visual flicker or re-ordering unrelated items.
“Only” Shortcut and Clear-All Controls
Given a specific chip is visible, when the user invokes the “Only” action on that chip (tap on mobile; Alt+Click on desktop), then all other chips are deselected and only that chip remains selected, updating the list within 300 ms. Given any filters are active, when the user activates “Clear All,” then all filters are deselected, the full arrivals list is shown within 300 ms, and a brief confirmation state indicates filters are cleared. Given an “Only” action is applied, when the user taps the same chip again, then that chip toggles off and the view returns to the unfiltered list within 300 ms. Given accessibility needs, when “Only” and “Clear All” are displayed, then their touch targets are at least 44×44 px and are keyboard focusable with visible focus states.
Keyboard Shortcut Controls for Desktop Dispatchers
Given the arrivals board has focus, when the user presses Ctrl/Cmd+K, then keyboard focus moves to the filter bar within 100 ms. Given the filter bar has focus, when the user presses Arrow keys, then focus moves between chips in visual order; when Space is pressed, the focused chip toggles selection; when Alt+Enter is pressed, the focused chip triggers “Only.” Given any filters are active, when the user presses Esc, then all filters are cleared and the full list is shown within 300 ms. Given shortcut usage, when an action occurs, then an aria-live polite announcement describes the change (e.g., “Brand X selected,” “Filters cleared”) for screen readers.
Sticky Filter Persistence per Device/User
Given a signed-in dispatcher selects filters on Device A, when they reload or reopen the board within 30 days, then the same filters restore automatically for that user on Device A. Given the same user signs in on Device B with different saved filters, when they open the board, then Device B’s last-used filters are applied and do not overwrite Device A’s filters. Given a user selects filters in a private/incognito session, when the session is closed, then no filters are persisted. Given a user selects filters, when they explicitly use “Clear All,” then the persisted state is updated to the cleared state.
Display-Lock Mode for Hallway Monitors
Given display-lock mode is enabled, when any touch, mouse, or keyboard input targets the filter controls, then no filter state changes occur and a non-intrusive lock indicator is shown. Given display-lock mode is enabled, when the page reloads or the device wakes from sleep, then lock mode remains active until explicitly unlocked. Given display-lock mode is enabled, when the user performs the configured unlock action (3-second long-press on the lock icon or entering a 4-digit PIN), then filter controls become active again within 300 ms. Given display-lock mode is disabled, when an attempt is made to enable it, then the lock state changes only after a deliberate two-step action to prevent accidental activation.
Real-Time Count Chips Update
Given count chips are visible on brand and app filters, when new orders arrive or existing orders complete, then the counts update within 1 second of the underlying event without requiring a manual refresh. Given no filters are active, when counts are displayed, then each chip shows the total number of open orders for that brand or app, and the sum across brands equals the total open orders (allowing for orders associated with multiple brands/apps as per data model rules). Given filters are active, when counts are displayed, then each chip also reflects the number of open orders currently visible that match that chip, distinguishable from the global count by label or style (e.g., “5 (2 shown)”). Given a count update occurs, when a user views the list, then the list and counts remain consistent (no ghost items with zero count and vice versa).
Performance and Input Responsiveness on Low-Cost Hardware
Given a low-cost device (e.g., Chrome on Intel Celeron N-series with 4 GB RAM or Android tablet with 2 GB RAM), when a user toggles any filter, then UI feedback (chip state and list update) occurs within 300 ms p95 and 150 ms median over a dataset of 1,000 open orders. Given continuous real-time updates (up to 10 order events per second), when filters are active, then frame rate remains ≥30 FPS p95 with <10% dropped frames during scrolling. Given heavy usage, when toggling filters repeatedly for 60 seconds, then memory growth attributable to filtering remains <200 MB and is reclaimed within 5 seconds after idle. Given both input modalities, when using touch or mouse, then all filter controls are operable, with touch targets ≥44×44 px and hover/focus states visible within 75 ms; no action requires precision tapping smaller than 44 px. Given network latency up to 300 ms, when new events arrive, then the board maintains correctness without missed or duplicated items and applies filters deterministically.
Saved Views and Hotkeys
"As a shift manager, I want saved views for my station so that I can switch the board to our workflow with a single tap at the start of a rush."
Description

Enable staff to create and recall named presets that bundle brand/app filters (e.g., “VirtualBrand-A + Uber Eats”). Allow per-user and per-station scopes, set a default view on sign-in, and expose hotkeys and touchscreen tiles for instant switching. Provide a sharable URL parameter to load a view on boot (e.g., for a dedicated hallway display) and basic analytics events to measure view usage and optimize layouts.

Acceptance Criteria
Create and Save a Named View
Given a dispatcher has selected brand/app filters When they choose Save View and provide a unique name and select scope (User or Station) Then the view is saved and persists across sessions for that scope And it appears in the Saved Views list within 1 second Given a view with the same name already exists in the selected scope When the dispatcher attempts to save Then the save is blocked and a duplicate-name message is shown Given a saved view exists When the dispatcher chooses Overwrite on Save View Then the existing view is updated with current filters and confirmation is shown
Assign and Recall View via Hotkey
Given a saved view and an available hotkey When the dispatcher assigns a hotkey to the view Then the hotkey is stored and is unique within the user's context (same user and station) And reserved OS/browser combinations cannot be assigned Given a saved view has hotkey Alt+1 When the dispatcher presses Alt+1 while the arrivals board is focused Then the view applies within 300 ms and the active view label updates Given two views attempt to share the same hotkey in the same context When the dispatcher saves the assignment Then the assignment is rejected and the conflict is indicated
Default View Applied on Sign-In with Precedence
Given defaults may exist at Station and User scopes When a user signs in on a station Then the arrivals board applies the default view before first render using precedence: Station default > User default > System default And only one default can be set per scope; setting a new default clears the prior one Given no defaults are set When a user signs in Then the System default view (All) is shown
Load View from Sharable URL on Boot
Given a sharable URL with parameter ?view={viewSlugOrId} When the arrivals board loads from a cold start Then the specified view is applied before initial paint with no intermediate flicker Given the ?view parameter does not match any saved view in accessible scopes When the arrivals board loads Then the app falls back to default-view precedence and shows a non-blocking notice Given both a URL parameter and a configured default exist When the page loads Then the URL parameter takes precedence
Touchscreen Tiles for Instant View Switching
Given a station with a touchscreen display When Saved Views are available Then each saved view renders as a tappable tile (minimum 44x44 px) showing its name and scope icon Given the dispatcher taps a view tile When the tap is registered Then the view applies within 300 ms and the active tile is visually highlighted Given a tile represents a view that was deleted When the dispatcher taps it Then no switch occurs and the tile list refreshes to remove it
Per-User vs Per-Station View Scope Behavior
Given a dispatcher saves a view with User scope When they sign in on any station Then the view is visible under My Views and is editable/deletable only by that user Given a dispatcher saves a view with Station scope on Station A When any authenticated user is on Station A Then the view is visible under Station Views and can be recalled by any user on Station A Given a user is on Station B When viewing Station Views Then Station A's scoped views are not shown
Analytics Events for View Usage and Optimization
Given analytics is enabled When a user creates, updates, deletes, loads, or switches a view Then events are sent within 1 second with properties: view_id, view_name, scope, trigger_source (hotkey|tile|url|default|menu), station_id, user_id_hash, timestamp Given a user remains on a view for a period When they switch away or the session ends Then a view_session_end event is emitted with duration_ms and the same identifiers Given PII constraints When events are sent Then no raw PII (e.g., phone, email) is included; only hashed user_id per policy
Brand/App Detection Rules Engine
"As an operator, I want the system to reliably detect the brand and app for each order so that staff see the right band and badge without manual fixing."
Description

Build a configurable rules engine that infers virtual brand and delivery app from POS order data and SMS check-in metadata. Support precedence-ordered rules (e.g., channel IDs, menu tags, regex on brand fields), fallbacks for unknown sources, and an inline correction UI on the order card with audit logging. Provide health checks and alerting for unmapped sources, plus an admin console to test rules against sample payloads before deployment. Ensure mappings propagate in real time so color bands and badges are accurate at arrival.

Acceptance Criteria
Precedence-Based Brand/App Inference from POS and SMS Metadata
Given a configured ruleset with precedence order [channel_id > menu_tag > regex] And rules exist for channel_id=DOORDASH, menu_tag=Brand A, and regex /Brand\sA/i on brand fields And an order payload contains channel_id=DOORDASH, menu_tag=Brand A, and brand_field="Brand A - Uber" When the rules engine evaluates the order Then the inferred app is DoorDash and the inferred brand is Brand A And the evaluation trace records matched rule IDs, precedence level, and matched values And if multiple rules match at the same precedence level, the first rule by defined order is applied deterministically And if no rule matches at a precedence level, evaluation proceeds to the next precedence level And P95 evaluation latency is <= 100 ms at 50 QPS sustained load
Real-Time Mapping Propagation to Arrivals Board (Color Bands & Badges)
Given an open arrivals board session and a mapped order awaiting pickup And the order has inferred brand and app When the customer clicks the SMS "I'm here" link Then the arrivals board displays the correct color band and app badge for the order within 1 second of the check-in timestamp And brand/app filter toggles immediately include the order in the corresponding filtered views And if the client was offline at the moment of update, the UI backfills the correct band and badge within 5 seconds of reconnection
Fallback Handling for Unknown or Partially Mapped Sources
Given an order payload with no matching rules at any precedence level When the rules engine evaluates the order Then the order is assigned brand=Unmapped and app=Unmapped with a neutral gray band and a "?" badge on the arrivals board And an alert event type=unmapped_source including source identifiers and sample payload is emitted within 5 seconds And the order remains visible and routable in the UI And the unmapped source appears in the admin console queue for mapping action
Inline Brand/App Correction on Order Card with Audit Trail
Given an arrivals board with an order displayed When a dispatcher opens the inline correction UI and selects a new brand and/or app Then the order card updates to the new color band and badge within 1 second And the correction persists across clients and page reloads And an immutable audit record is written with order_id, before/after values, user_id, timestamp, and reason (optional) And inline correction does not modify global rules until an admin explicitly deploys a rule change
Admin Console Rule Testing and Safe Deployment
Given an admin opens the rules console with draft editing permissions When the admin defines/edits rules (channel_id, menu_tag, regex) and sets explicit precedence ordering And pastes a sample POS order payload and SMS metadata Then clicking Test returns the inferred brand/app and a step-by-step rule trace without affecting production And invalid regex or rule syntax is blocked with inline validation errors before save And clicking Deploy publishes the new ruleset within 10 seconds, versions it, and logs actor, timestamp, and diff And the console provides one-click rollback to the previous version with immediate effect
Health Checks and Alerting for Unmapped Rates and Engine Failures
Given the rules engine service is running When the /health endpoint is queried Then it returns 200 with build version, active ruleset version, rule count, last deploy time, and evaluation latency stats (P50/P95) And if the 5-minute unmapped rate exceeds 2%, an alert is sent to configured channels with top offending sources and sample payload links And if rules evaluation errors exceed 0.5% in a 5-minute window or the service becomes unreachable, an alert is sent and status flips to Degraded And all health checks and alerts are recorded in the admin activity log with timestamps
Accessibility and Performance Compliance
"As a staff member with varying lighting conditions, I need readable, accessible bands and responsive controls so that I can avoid mistakes during peak periods."
Description

Guarantee colorblind-safe palettes and WCAG AA (or better) contrast for text and icons over color bands, with focus-visible states for all filter controls and ARIA labels for screen readers. Enforce a performance budget so additional color and icon rendering adds no perceptible lag: initial board paint under 1 second on commodity hallway devices, frame updates under 50 ms during rush, and lazy loading for icon assets. Add offline caching for icon sets and palette definitions, and instrument telemetry to detect missed pings or filter misconfiguration impacting throughput.

Acceptance Criteria
Contrast and Colorblind Safety on BrandBands
Given the arrivals board with BrandBands enabled and a full set of brands/apps, When text and icon badges render over their color bands, Then the contrast ratio between any foreground text/icon and its band background is >= 4.5:1 for normal text and >= 3:1 for large text (WCAG 2.1 AA), And automated accessibility audits report zero WCAG contrast violations on the board, And each brand/app is identifiable without color by a unique icon shape or text label, And under simulated deuteranopia, protanopia, and tritanopia, all brands/apps remain distinguishable by non-color cues with no more than 0 identification failures in automated checks across the full set.
Keyboard Focus Visibility for Filter Controls
Given keyboard-only navigation on the arrivals board, When the user tabs through one-tap filters and quick toggles, Then every focusable control displays a visible focus indicator with contrast >= 3:1 and a minimum 2px outline that is not obscured, And controls are operable via Enter and Space and reflect state via aria-pressed/aria-checked, And tab order follows visual order without traps; Shift+Tab reverses correctly, And no control requires a pointer to operate the primary action.
Screen Reader Semantics for Filters and Badges
Given NVDA (Windows/Chrome) and VoiceOver (iOS/Safari), When navigating the filters, toggles, and icon badges, Then each control exposes an accessible name matching its visible label, the correct role (button/switch), and an accurate state announcement (pressed/on/off), And each brand/app icon badge has a programmatic label including the brand/app name (and bay number if present), And the arrivals list is contained in a landmark/region labeled "Arrivals Board" and dynamic row updates are announced via a polite live region without re-announcing unchanged content.
Initial Paint Performance on Hallway Devices
Given a representative commodity hallway device (e.g., 2GB RAM Android tablet, Chrome latest) on stable Wi‑Fi and a cold cache with 50 active orders and 25 brands/apps, When the arrivals board route loads with BrandBands enabled, Then initial board paint showing bands and first viewport of rows occurs within <= 1,000 ms from navigation start, And no icon assets are requested before first paint (critical path excludes icons), And the additional JS/CSS for BrandBands does not exceed 120 KB gzip combined on initial load.
Rush-Hour Update Performance Budget
Given a peak update rate of 10+ order/arrival updates per second and active filter toggling, When the board applies updates and re-renders bands and badges, Then the 95th percentile render/update task duration is <= 50 ms over a 5-minute run, And dropped frames are < 1% and there are zero main-thread long tasks > 50 ms attributable to BrandBands rendering, And memory usage attributable to BrandBands remains stable (no continuous growth > 10% over the run).
Lazy Loading and Offline Caching of Icons and Palettes
Given a cold cache on first load, When the arrivals board first paints, Then no more than 10 KB of icon data is loaded before first paint and icon assets load lazily only for visible brands/apps, And cumulative layout shift from late-loading icons is <= 0.1 total and <= 0.01 per icon, And after first successful load, icon sets and palette definitions are cached via Service Worker for at least 7 days, And when offline with a warm cache, icons and palettes render with zero network requests to icon/palette endpoints.
Telemetry for Missed Pings and Filter Misconfiguration
Given telemetry is enabled in production, When arrivals occur and filters are applied, Then events are emitted for ping receipt, visibility state (visible/filtered), filter toggles, and render timings, And a "missed ping" is recorded when a pinged arrival remains non-visible for > 30 seconds due to filter state, with rate aggregated per hour, And an alert is generated when missed ping rate exceeds 2% or throughput (visible pings processed per hour) drops > 10% versus 7-day baseline, And telemetry excludes PII and supports configurable sampling.

Courier Stack

Automatically detects when the same driver is collecting multiple orders across different brands/apps and collapses them into one unified tile. Shows a combined checklist, consolidated payment/verification state, and precise shelf/door pick points—cutting back‑and‑forth and clearing hallways faster.

Requirements

Cross-App Courier Identity Resolution
"As a front-of-house staffer, I want the system to recognize when a driver is picking up multiple orders so that I can stage and hand off everything in one interaction."
Description

Automatically detect when the same courier is collecting multiple orders across brands/apps for the same location and time window by correlating SMS link device fingerprints, originating phone numbers, arrival timestamps within a geofence, and shared courier/order metadata (e.g., courier name from aggregator, vehicle color/plate if supplied). Compute a confidence score and auto-merge above a threshold; surface a safe manual-merge option for staff when confidence is borderline. Maintain strict PII minimization and short-lived tokens; never persist raw device fingerprints beyond the session. Provide clear error handling and un-merge capabilities to correct false positives without losing order state.

Acceptance Criteria
Auto‑Merge on High Confidence Detection
Given two or more orders for the same location are detected within the configured geofence and time window And identity signals (in-session device fingerprint match, originating phone number match or verified alias, matching courier metadata such as name or vehicle plate/color when available) produce a confidence score >= 0.85 When the arrival event is processed Then the orders are auto-merged into a single courier tile And the tile shows a combined checklist, consolidated payment/verification state, and unified shelf/door pick points And no manual confirmation is required And a privacy-safe merge event is recorded with the confidence score and contributing signals (no raw fingerprints persisted)
Manual Merge Prompt on Borderline Confidence
Given two or more orders for the same location fall within the configured geofence and time window And identity signals produce a confidence score between 0.60 and 0.84 inclusive When the system evaluates the courier identity Then a manual-merge suggestion is displayed to staff with a clear reason summary and the confidence score And staff can confirm or decline the merge And if confirmed, the orders merge into a single courier tile with combined checklist, payment/verification state, and pick points And if declined, the orders remain separate and no merge occurs And all actions are audit-logged without PII
Un‑Merge Without State Loss
Given an existing merged courier tile containing two or more orders When staff selects Un-merge Then the original order tiles are restored with their pre-merge states intact (payment status, checklist progress, timestamps, pick points) And the un-merge operation completes within 2 seconds And a privacy-safe audit entry records the un-merge with the prior merge transaction ID
PII Minimization and Token Lifecycle
Given a courier session initiated via an SMS 'I'm here' link When a device fingerprint is generated for identity resolution Then only a short-lived tokenized representation is held in memory for the session And no raw device fingerprint components are persisted to databases, logs, or analytics And the token expires on session end or after 30 minutes of inactivity, whichever occurs first And a storage scan confirms no raw fingerprint values exist after session termination
Confidence Score Determinism and Thresholds
Given a fixed set of identity signals for two orders When the confidence score is computed across multiple service instances Then the score is deterministic within ±0.01 And the auto-merge threshold is set to 0.85 and the manual-merge prompt threshold is set to 0.60 by default And thresholds can be configured per location without code changes And geofence radius and time window constraints are enforced during scoring
Error Handling and Idempotent Merge Operations
Given a transient network error or concurrent merge attempt occurs during an auto-merge or manual-merge confirmation When the merge operation is executed and retried Then the result is idempotent: either one merged tile exists or the orders remain as separate tiles, with no partial/duplicate merges And the user interface surfaces a clear, actionable error message if manual intervention is needed And no order state (payment status, checklist progress, timestamps) is lost or corrupted And the failure and recovery are recorded in a privacy-safe audit log
Unified Courier Tile with Combined Checklist
"As a runner, I want a single, expandable tile that lists all orders for the same courier so that I can verify items and complete the pickup without jumping between screens."
Description

Collapse detected multi-order pickups into a single queue tile that displays courier identity, vehicle/bay assignment, total order count, and an expandable per-order checklist (brand, order name/ID, hot/cold flags). Support quick actions at both levels: Mark All Ready, Mark All Handed Off, and per-order toggles. Show real-time status chips (Ready, Delayed, Missing Item) and highlight blockers. Ensure the tile is accessible (keyboard/ARIA), responsive on tablets, and fits within the existing CurbPing queue layout without increasing cognitive load.

Acceptance Criteria
Unified Tile Creation on Multi-Order Detection
Given the same courier identity is matched across two or more active pickup orders within a 10-minute window by phone number, license plate, or courier ID And all matched orders are in an in-queue state (Awaiting Pickup or Ready) When the queue view renders or real-time events arrive Then the system collapses those orders into a single unified courier tile within 2 seconds And replaces the individual tiles with the unified tile without changing their relative queue position by more than ±1 slot And the collapsed tile shows courier display name (or "Courier" if unknown), vehicle and/or bay assignment if available, and total order count And the tile exposes an expand/collapse control with an accessible name "Show N orders" / "Hide orders"
Combined Checklist and Metadata Display
Given a unified courier tile is expanded When it renders the order list Then each child order row displays brand name, customer name or order ID, hot/cold flags, and shelf/door pick point And the list is sorted by promised-ready time ascending And text truncates with ellipsis after one line for brand and name/ID while the full value is available to assistive tech and on hover/focus tooltip And the collapsed tile first line shows only courier identity, bay/vehicle (if any), total order count, and a single aggregate status chip And the consolidated payment/verification state is computed and displayed as: if any order is unpaid => "Payment Pending"; else if any order requires ID => "Verify ID"; else => "Verified"; per-order exceptions are indicated on their rows
Tile-Level Quick Actions with Blocker Handling
Given a unified courier tile with N≥2 child orders When the operator taps "Mark All Ready" Then all child orders transition to Ready within 1 second and display Ready chips And the aggregate chip updates to Ready Given any child order is Delayed or has Missing Item When the operator taps "Mark All Handed Off" Then the system surfaces a confirmation dialog listing blocking orders and reasons And completion proceeds only upon explicit confirmation "Confirm Hand Off (N orders)" When "Mark All Handed Off" is confirmed Then all child orders transition to Handed Off within 1 second and the unified tile is removed from the queue
Per-Order Status Toggles and Aggregate Status Logic
Given an expanded unified tile When the operator sets a child order status to Ready, Delayed, or Missing Item Then the child row updates its status chip immediately and persists to the backend within 1 second (success toast or inline confirmation) And the aggregate tile chip updates within 1 second according to priority Missing Item > Delayed > Ready > Mixed And any order with Missing Item is visually highlighted and pinned above non-blocked orders And the primary handoff action label reflects readiness, e.g., "Hand Off (Ready N of M)"
Real-Time Sync and Concurrency for Unified Tile
Given any child order status changes via external integration or another device When the change occurs Then the unified tile reflects the update within 3 seconds without page refresh And total order count, aggregate chip, and checklist remain accurate with no duplicate rows Given a child order is canceled or reassigned to another courier Then it is removed from the tile within 3 seconds and a non-blocking toast appears And if only one order remains, the unified tile converts to a standard single-order tile within 2 seconds; if zero, the tile is removed And concurrent edits resolve via last-write-wins by server timestamp and the UI displays the final state without flicker
Accessibility: Keyboard, ARIA, and Announcements
Given a keyboard-only user navigates the queue When tabbing through interactive elements Then the unified tile, expand/collapse, quick actions, and per-order controls are reachable via Tab/Shift+Tab in a logical order And Space/Enter activate the focused control; Arrow keys move between child rows when focus is within the list And focus is visibly indicated (≥3:1 contrast); text/icon contrast meets WCAG 2.2 AA (text 4.5:1) And live status updates (Ready, Delayed, Missing Item, count changes) are announced via aria-live="polite" And roles/names/states are exposed for tiles (role="group"), lists (role="list"), items (role="listitem"), toggles/checkboxes, and buttons with accessible names And closing any dialog returns focus to the invoking control
Responsive Queue Fit and Performance on Tablets
Given a tablet device 768–1024 px width at 1x–2x DPR When viewing the queue Then the unified tile renders without horizontal scroll; collapsed tile height ≤ 1.5× the single-order tile; touch targets ≥ 44×44 px And expanding the tile uses in-tile scrolling for lists > 6 items and does not cause the page to jump And initial tile render time ≤ 200 ms after detection; scrolling performance ≥ 50 FPS with 50 tiles including 10 unified tiles And the queue maintains its two-column layout in landscape if previously supported and does not add more than one extra row compared to the same orders uncollapsed
Consolidated Verification and Payment State
"As a shift lead, I want a single place to see and resolve all payment/verification requirements for a courier’s stacked orders so that handoff is fast and compliant."
Description

Aggregate and display a consolidated verification/payment view for stacked orders, pulling signals from POS/aggregator integrations and first-party orders. Show a combined state (e.g., 2 of 3 verified) with per-order details for OTP codes, ID check requirements, or unpaid balances. Allow one-time code entry to apply to all eligible orders and clearly flag exceptions requiring separate checks. Provide staff override with mandatory reason codes and write all actions to an audit log. Handle partial failures without blocking completion of verified orders.

Acceptance Criteria
Consolidated State Summary on Stacked Courier Tile
Given a courier stack with N orders across multiple sources (POS, aggregator, first‑party) When verification/payment signals are received or change Then the tile displays a combined summary in the format "x of N verified" and "Total unpaid: $T" within 2 seconds of the latest signal And counts and totals match the underlying per‑order states And orders in unknown state are treated as Pending and not counted as verified And when stack membership changes (order added/removed), the summary recomputes within 2 seconds and reflects the new N
Per‑Order Verification and Payment Detail Rendering
Given a stacked courier tile is opened When viewing per‑order details Then each order shows its verification requirements (OTP, ID check) and current status with timestamp And each order shows payment state (Paid/Unpaid) and any unpaid balance amount to the cent And aggregator orders display platform badge and external order ID; first‑party/POS orders display local order ID And OTP values are never fully displayed; after verification only the last 2 digits are visible And visual indicators for Verified, Pending, and Exception states are distinct and labeled
One‑Time Code Applied Across Eligible Orders
Given multiple orders in a courier stack share the same OTP group from an external source When a staff member enters a valid one‑time code once on the consolidated view Then all eligible orders in that OTP group are marked Verified within 2 seconds And ineligible orders remain unverified and are clearly flagged as requiring separate checks And the combined summary updates to reflect the new verified count And OTP entry enforces a maximum of 3 failed attempts per 5 minutes with a user‑visible lockout message
Exception Flagging and Separate Checks
Given a courier stack contains orders with different verification requirements When an order requires a distinct OTP, ID check, or has an unpaid balance Then that order is flagged with an Exception badge and a short reason (e.g., "ID required", "Unpaid $12.45") And tapping/clicking the flag reveals required next steps And Exception orders are excluded from auto‑verification effects and must be resolved individually or via override And the system prevents completing handoff for a flagged order until its requirement is satisfied or overridden
Staff Override with Mandatory Reason and Permissions
Given a staff user attempts to override verification for one or more orders When the user lacks the Override permission Then the Override action is disabled with a tooltip explaining required role When a permitted user initiates Override Then the user must select a reason code from a predefined list and enter a free‑text note of at least 10 characters before proceeding And upon confirmation, the affected orders are marked Verified (Overridden), the combined summary updates, and exceptions are cleared for those orders
Comprehensive Audit Logging of Verification and Overrides
Given any verification state change occurs (auto‑signal, OTP entry, payment confirmation, override, failure) When the event is processed Then an audit record is written containing timestamp, actor (user or system), action type, affected order IDs, source system, before/after states, terminal/device ID, and request identifier And OTP values are redacted except for the last two digits And the record is immutable and queryable in the admin view within 5 seconds of the event And partial failures include error codes/messages from the source integration
Partial Verification Completion Without Blocking
Given a courier stack has some orders verified and others pending/exception When the staff chooses to complete only verified orders Then the system allows handoff for verified orders without blocking, leaves unverified orders in the stack, and prints/records only the completed order IDs And the combined summary decrements N accordingly and continues to show remaining pending items And a "Complete Verified" action is available when at least one order is verified and at least one remains unverified
Precise Shelf/Door Pick-Point Mapping
"As a packer, I want exact shelf slots and exit doors for each order in a courier’s stack so that I can pull items in the fastest route and clear the hallway quickly."
Description

For each order in a courier stack, display precise pick points including shelf label/slot and the optimal exit door. Support location-specific configuration of shelf maps and door names, QR-coded shelf labels for rapid confirmation, and visual cues to guide the fastest pull path. Suggest an ideal retrieval sequence to minimize steps and traffic. Provide graceful fallback when a shelf map is misconfigured or offline, and allow staff to quickly reassign pick points during rush.

Acceptance Criteria
Show Pick Points per Order in Courier Stack
Given a courier stack with 2–10 orders at a location with an active shelf map and door configuration When the unified stack tile is opened Then each order row displays its shelf label, shelf slot, and optimal exit door within 1 second And the shelf label and door name exactly match the location’s configuration And orders missing any mapping display a clear "Unmapped" tag and a Reassign action And changes to an order’s pick point are reflected on all open clients within 2 seconds
Location-Specific Shelf and Door Configuration
Given a device authenticated to Location L with shelf-map version vX and configured door names When the Courier Stack view is loaded or refreshed Then the app uses L’s vX shelf coordinates and door names for all mapping and pathing And updates to configuration published in admin are applied within 60 seconds or on manual refresh And configuration with duplicate shelf labels or door names is rejected and surfaced with a validation error message to the user
QR Code Confirmation of Shelf Picks
Given an order assigned to shelf S and slot N with a QR-coded label encoding S-N When a staff member scans the QR from within the stack tile for that order Then the system validates the code against the order’s expected pick point and marks the pick as confirmed within 1 second And on mismatch, the scan is rejected with a clear reason and no status change And in offline mode, staff can enter the alphanumeric code and receive the same validation And all scan attempts (success/fail) are logged with timestamp and user identifier
Visual Path Guidance and On-Screen Cues
Given a courier stack with at least two mapped orders and a selected exit door D When Guide Mode is activated Then the UI displays a visual path with arrows and numbered badges from the staging area through each target shelf in sequence and to door D And shelves in the sequence are highlighted and correspond to order badges And color cues are consistent: green = next pick, gray = completed, red = unmapped/blocked And changing the selected exit door updates the guidance within 500 ms
Optimal Retrieval Sequence Suggestion
Given a courier stack of up to 10 orders with known shelf coordinates and a selected exit door When "Suggest sequence" is tapped Then the system proposes an order retrieval sequence that minimizes total walking distance using the configured coordinates And for the standard test layouts, the proposed path length is within 10% of the computed optimal And ties are broken by earliest ready time And the sequence is generated in under 300 ms on reference hardware And staff can accept the sequence with one tap or manually reorder via drag-and-drop
Fallback and Manual Reassignment During Outage
Given the shelf map fails to load or is detected as misconfigured for Location L When a courier stack is opened Then orders display text-only pick points if available; otherwise show "Unmapped" And staff can reassign shelf label/slot and exit door for any order in 5 seconds or less via a searchable list And reassigned pick points persist for that stack and sync to all clients within 2 seconds And a non-blocking banner indicates fallback mode and provides a retry action And when the map recovers, manual overrides remain for existing stacks while new stacks use the recovered map
Courier Link Orchestration via SMS
"As a courier, I want to confirm my multi-order pickup in one SMS thread so that staff prepares everything together and I don’t have to repeat myself at each window."
Description

When the same device opens multiple 'I’m here' links within a short window, send a confirmation prompt asking the courier to confirm they are picking up multiple orders, and merge accordingly. Offer a simple SMS reply (e.g., YES/NO) to resolve ambiguous matches. Include a secure deep link for the courier to view a read-only pickup summary when allowed by the merchant, reducing questions at the door. Respect rate limits, consent, and opt-out across brands to remain compliant and non-intrusive.

Acceptance Criteria
Multi-Link Detection Triggers SMS Confirmation
Given the same device session opens “I’m here” links for two or more distinct orders within the configured detection window (default 10 minutes) When the second link is opened within that window Then send exactly one SMS confirmation prompt to the device’s phone number within 5 seconds that asks to confirm multi-pickup (YES to merge, NO to keep separate) and lists each brand and order count And mark the involved orders with a pending multi-pickup state And suppress any additional confirmation prompts for that device for the configured cooldown (default 10 minutes) while a prompt is active
YES Reply Merges Orders into Unified Tile
Given a pending multi-pickup confirmation exists for N orders across one or more brands for the same device When the courier replies YES via SMS within the configured response window (default 5 minutes) from the same message thread Then merge the orders into a single unified courier tile within 3 seconds And display a combined checklist that includes all required items across orders without duplication And present a consolidated payment/verification state that applies the strictest requirement among included orders And show precise shelf/door pick points per brand within the unified tile And send a confirmation SMS stating the orders have been merged and the total count And write an audit log entry with timestamp, device identifier, merged order IDs, and brands
NO Reply Keeps Orders Separate
Given a pending multi-pickup confirmation exists for N orders When the courier replies NO via SMS within the configured response window Then keep the orders as separate tiles and clear the pending multi-pickup state And send an acknowledgment SMS that orders remain separate And write an audit log entry with timestamp, device identifier, and order IDs indicating separation
Invalid Reply or No Response Handling
Given a pending multi-pickup confirmation prompt has been sent When the courier replies with any value other than YES or NO Then do not merge the orders and send a single clarification SMS listing valid replies (YES/NO) and including the secure summary link if permitted And do not send more than one clarification message per prompt When no reply is received within the configured response window (default 5 minutes) Then expire the prompt, leave orders unmerged, and record the expiration in the audit log
Secure Pickup Summary Deep Link
Given at least one involved merchant has enabled sharing a read-only pickup summary When the confirmation or clarification SMS is sent Then include a signed, single-use deep link token that expires after the configured TTL (default 60 minutes) and cannot be used after expiry or revocation When the courier opens the link before expiry Then display a read-only summary showing brands, short order codes, combined checklist items, consolidated verification requirement, and precise pick points And do not display customer PII (full names, phone numbers, or payment details) And log each link open with timestamp and device info
Consent and Cross-Brand Rate Limit Compliance
Given a device/phone number is marked as opted out (STOP) with any CurbPing brand When a multi-link detection occurs Then do not send any confirmation or clarification SMS and record a suppressed-send audit event Given a device/phone number is not opted out When sending any confirmation or clarification SMS Then include HELP/STOP instructions in the message footer and honor STOP immediately for future messages And enforce rate limits: maximum one confirmation prompt per device per 10 minutes and maximum three multi-pickup-related SMS per device across brands per 30 minutes; suppress sends beyond these limits and log suppression
Batch Handoff Workflow and Audit Trail
"As a manager, I want bulk handoff controls with a clear audit trail so that we move lines faster while maintaining accountability and accuracy."
Description

Enable bulk actions for stacked orders: print or display a combined pick ticket, Mark All Ready, and Mark All Handed Off, with support for partial handoffs when one order is delayed. Capture staff identity, timestamps, bay used, and any override reasons in an immutable audit log. Provide quick undo for mistaken taps and surface a reconciliation view to resolve discrepancies after the rush. Integrate with existing bay assignment logic to avoid conflicts when multiple couriers arrive simultaneously.

Acceptance Criteria
Combined Pick Ticket for Stacked Orders
Given a courier stack contains two or more orders across different brands When a staff member selects "Print/Display Combined Pick Ticket" from the unified tile Then a single ticket is generated within 2 seconds that lists, for each order: brand name, order ID, customer name or masked identifier, payment/verification state, shelf/door pick point, bay assignment if set, and an item checklist And the ticket totals show the number of orders and total item count And the ticket is accessible from the stack timeline and remains available for reprint until all orders are handed off
Bulk Mark All Ready
Given a courier stack has at least one order in a pre-ready state (Preparing or Staged) When a staff member taps "Mark All Ready" Then all eligible orders transition to Ready status in a single operation and the unified tile reflects the updated count within 1 second And any ineligible orders (e.g., missing required verification) remain unchanged and are listed in a confirmation modal/toast with specific reasons And an audit record is written capturing staff identity, timestamp, stack ID, and the list of order IDs updated
Bulk Mark All Handed Off with Partial Handoff
Given a courier arrives for a stack where one or more orders are not Ready When a staff member initiates "Mark All Handed Off" Then the system preselects only Ready orders and prompts to confirm a partial handoff And on confirmation, selected orders move to Handed Off, unselected remain unchanged, and the unified tile updates remaining count immediately And per-order audit entries capture staff identity, timestamp, bay number used, and a partial handoff indicator
Immutable Audit Trail Capture
Given any bulk action (print/display, mark all ready, mark all handed off, undo) is executed When the action completes Then an append-only audit entry is created that includes: action type, staff identity, stack ID, affected order IDs, previous and new statuses per order, timestamp to millisecond precision, bay number if applicable, and any override reason entered And audit entries cannot be edited or deleted via UI or API; attempts are rejected and logged And audit entries are viewable in the stack’s audit view and filterable by date range and action type
Quick Undo for Mistaken Bulk Actions
Given a bulk action on a stack has completed within the last 60 seconds and none of the affected orders have progressed beyond Handed Off When a staff member taps "Undo" Then the system reverts all affected orders to their prior states and restores prior bay assignments where available And if a prior bay is no longer available, the system offers to reassign using existing bay logic or proceed without a bay, and records the choice And an audit entry is appended linking the undo to the original action
Reconciliation View for Post-Rush Discrepancies
Given there exist orders with discrepancies between expected status from the audit trail and current order status When a staff member opens the Reconciliation view Then discrepancies are listed grouped by stack with columns for order ID, expected status, current status, last action, and responsible staff And staff can resolve each by selecting "Mark Handed Off", "Reopen to Ready", or "Dismiss with Reason" And on resolution, the item is removed from the list and an audit entry is created with the resolution and reason
Bay Assignment Conflict Avoidance During Simultaneous Arrivals
Given two or more couriers associated with different stacks arrive within a short window and are assigned overlapping bays When staff performs bulk Ready or Handed Off actions Then the system validates bay availability and ensures no two active handoffs share the same bay at the same time And if a conflict is detected, the system prompts to reassign using existing bay logic before completing the action And the final bay used per order is captured in the audit trail
Real-Time Merge Latency and Idempotency Guarantees
"As an operator, I want courier stacks to appear instantly and reliably so that staff can act without delay during peak times."
Description

Ensure courier stack detection and tile updates occur in near real-time, targeting under 1 second from arrival ping to unified tile render. Make merge operations idempotent so repeated pings or retries do not create duplicates or corrupt state. Provide observable metrics (merge latency, false-positive rate, merge confidence distribution), alerting on degradation, and graceful degradation paths that fall back to separate tiles when services are impaired. Include load testing to validate peak rush performance.

Acceptance Criteria
Sub-1s Merge Latency on Arrival Ping
Given a courier arrival ping with valid order identifiers and location When the system evaluates and merges eligible orders into a unified tile Then the unified tile renders on the staff dashboard within 1000 ms end-to-end at p95 and within 1500 ms at p99, measured from ping receipt to tile paint acknowledgment And Given normal network conditions (<150 ms RTT) When 100 consecutive arrivals occur Then at least 95 meet <=1000 ms and at least 99 meet <=1500 ms with 0 timeouts
Idempotent Merge on Duplicate Pings/Retries
Given duplicate or retried arrival pings for the same courier/order set within a 2-minute window When processed concurrently or sequentially Then exactly one unified tile exists and no duplicate tiles are created And the tile’s checklist and verification state are preserved without duplication And Given a merge already completed When the same merge key is reprocessed Then the operation is a no-op, the tile version does not increment, and side effects (notifications/audit) are emitted once
Graceful Degradation to Separate Tiles on Service Impairment
Given the merge service returns 5xx or times out 3 times within 2 seconds When an arrival ping is received Then separate per-order tiles are rendered within 1000 ms and marked as Degraded Merge And Given service health is restored When subsequent arrivals occur Then new arrivals use unified tiles while existing separate tiles remain actionable and can be manually merged without loss of checklist or verification data
Observability: Metrics Exposure and Accuracy
Given the system is operating When scraping the metrics endpoint Then metrics are exposed with labels {store_id, brand_id, region, merge_key, courier_id_hash}: merge_latency_ms (histogram), merge_attempts_total, merges_success_total, merges_idempotent_dedup_total, merges_false_positive_total, merges_confidence_bucket, merge_error_total And Given N test arrivals with known timestamps When metrics are scraped after processing Then counts match N and latency percentiles are within ±5% of ground-truth measurements
Alerting on Latency, Errors, and False Positives
Given production traffic When merge_latency_ms p95 > 1000 ms for 5 consecutive minutes OR merges_false_positive_total / merges_success_total > 0.5% over a 15-minute window OR merge_error_total rate > 1% over 5 minutes Then a P2 alert pages the on-call within 1 minute with a runbook link and recent metric snapshots And When the violating metric remains below thresholds for 10 minutes Then the alert auto-resolves and a recovery notification is sent
Peak Rush Load Test Performance
Given a load test of 500 concurrent couriers across 50 stores for 10 minutes (>=10 pings/sec sustained, bursts to 50 pings/sec) When courier stack detection and merging executes Then p95 latency <= 1000 ms, p99 <= 1500 ms, error rate < 0.5%, zero dropped events, and no duplicate tiles And Then service CPU < 70%, memory < 75%, and queue depth drains to <10 within 30 seconds after burst completion
False-Positive Control and Confidence Thresholding
Given multiple orders are candidates for a single courier across brands/apps When merge confidence is computed Then merges occur only when confidence >= 0.85 and verification states are compatible; otherwise tiles remain separate And Given a labeled validation set of 10,000 courier pickups When evaluating the merge algorithm at threshold 0.85 Then false-positive merge rate <= 0.5% and false-negative rate <= 5%

SLA Watch

Normalizes each delivery app’s pickup timers and ETAs into a single, clear countdown on the board. Flags at‑risk orders with gentle escalations to Expo, suggests reprioritization, and tracks on‑time performance by brand/app so late pickups drop and ratings stay protected.

Requirements

Multi-App ETA Normalizer
"As an expo lead, I want all delivery app pickup timers normalized into one consistent countdown so that I can manage orders without juggling different app clocks or interpretations of SLA."
Description

Ingests pickup ETAs, countdowns, and SLA expectations from multiple delivery sources (e.g., DoorDash, Uber Eats, Grubhub) via available webhooks/APIs, aggregator integrations (e.g., Otter, Chowly, Deliverect), and an email/SMS parsing fallback, then maps them into a canonical time model. Applies store-configured prep buffers, grace periods, and drift adjustments to produce a single, continuously updated “time-to-pickup” value per order. Handles late/early driver signals and re-syncs on schedule changes, ensuring consistent timing semantics across apps without requiring additional hardware or app installs.

Acceptance Criteria
Normalize Direct App ETAs to Canonical Countdown
Given current time is 2025-08-21T12:00:00-04:00 And store config: prep_buffer=5m, grace_period=3m, drift_adjustments: DoorDash=+2m, UberEats=0m, Grubhub=-1m And DoorDash provides pickup_by=2025-08-21T12:15:00-04:00 And UberEats provides driver_eta=2025-08-21T12:10:00-04:00 And Grubhub provides countdown=20m When the normalizer processes these inputs Then adjusted_pickup_at values are DoorDash=12:17, UberEats=12:10, Grubhub=12:19 (store local time) And time_to_pickup values are DoorDash=12m, UberEats=5m, Grubhub=14m
Aggregator Re-Sync Updates Canonical Countdown on Schedule Change
Given current time is 2025-08-21T12:00:00-04:00 And store config: prep_buffer=7m, drift_adjustments: Otter=+1m And an Otter order has pickup_eta=2025-08-21T12:25:00-04:00 When the normalizer computes time_to_pickup Then time_to_pickup=19m When Otter sends a schedule change updating pickup_eta to 2025-08-21T12:35:00-04:00 Then the normalizer updates time_to_pickup to 29m within 2s of receipt
Fallback Email/SMS Parsing Produces Canonical Countdown and SLA Default
Given current time is 2025-08-21T12:20:00-04:00 And store config: prep_buffer=5m, SLA_pickup_window=25m, drift_adjustments: Fallback=0m And aggregator APIs are unavailable And for Order A, an email states "Pickup ETA 12:40 PM" When the parser extracts pickup_eta=2025-08-21T12:40:00-04:00 Then time_to_pickup for Order A is 15m And for Order B, an SMS says "Driver soon" (no parsable time) When the normalizer applies SLA fallback from order_placed_at=2025-08-21T12:10:00-04:00 Then time_to_pickup for Order B is 10m
Early/Late Driver Signals Adjust Canonical Countdown
Given store config: prep_buffer=5m And Order C has adjusted_pickup_at=2025-08-21T12:15:00-04:00 And current time is 2025-08-21T12:06:00-04:00 When an early driver arrival signal is received for Order C Then time_to_pickup for Order C becomes 0m immediately And Order D has adjusted_pickup_at=2025-08-21T12:10:00-04:00 And current time is 2025-08-21T12:20:00-04:00 with no arrival signal Then time_to_pickup for Order D is -15m
Continuous Update Cadence and Staleness Bound
Given an order with adjusted_pickup_at set such that time_to_pickup starts at 10m 00s When 5 seconds elapse Then time_to_pickup decreases by 5s (09m 55s to 09m 55s ±1s jitter) And any client subscription receives an updated value stamped within 2s of server time
Time Zone Normalization Across Sources
Given store time zone is America/Los_Angeles And current time is 2025-08-21T09:00:00-07:00 And store config: prep_buffer=5m, drift_adjustments: UberEats=0m And UberEats provides pickup_eta=2025-08-21T12:30:00-04:00 When the normalizer converts pickup_eta to store local time Then adjusted_pickup_at is 2025-08-21T09:30:00-07:00 And time_to_pickup is 25m
Unified Countdown Board
"As a kitchen expeditor, I want a single board that shows one countdown per order with clear states so that I can immediately see what needs attention and act quickly."
Description

Adds a clear, single-countdown view to the existing CurbPing Expo Board, showing remaining time to pickup per order with color states (On Track, Watch, At Risk, Late). Supports sorting by time remaining, filtering by brand/app, and grouping by bay or order type. Provides accessible large-format typography, high-contrast themes, and subtle animations that do not distract. Updates in real time as ETAs shift or orders are acknowledged, and surfaces key metadata (brand/app, driver status, bay assignment) inline for rapid triage.

Acceptance Criteria
Normalized Single Countdown per Order
Given orders from multiple apps each with an ETA and pickup target time, When the Expo Board loads or new orders arrive, Then each order displays exactly one countdown equal to (target pickup time − current time) with accuracy ±1 second. And the countdown displays mm:ss for durations under 60 minutes and h:mm for durations 60 minutes or more. And when the countdown reaches 0, Then the order state switches to Late and the timer counts up as +mm:ss. And no app-specific duplicate timers are shown for the same order.
Real-Time ETA and Acknowledgment Updates
Given an ETA change is received for an order, When the board processes the update, Then the affected order’s countdown and color state update on-screen within 1 second without full-board flicker. Given the order is acknowledged Ready or its bay assignment changes, When that event is received, Then inline metadata reflects the new status within 1 second. And the countdown visually ticks at 1 Hz with cumulative drift under 200 ms over any 5-minute interval.
Color States and Threshold Transitions
Using default thresholds: On Track if time remaining > 6:00; Watch if 6:00 ≥ time remaining > 3:00; At Risk if 3:00 ≥ time remaining ≥ 0; Late if time remaining < 0. Given the time remaining crosses a threshold, When the next render occurs, Then the state label and color update immediately and do not oscillate more than once within 3 seconds (3-second hysteresis). And each state is indicated by both color and a text label/icon so color is not the sole indicator. And all state text against its background meets WCAG 2.1 AA contrast (≥ 4.5:1).
Sort, Filter, and Group Controls
Given the user selects Sort by Time Remaining Ascending, When applied, Then orders sort ascending within 300 ms for up to 200 visible orders. Given the user toggles Descending, Then the sort reverses within 300 ms. Given the user applies a brand/app filter, Then only orders from selected brands/apps are shown and the visible count updates immediately. Given the user selects a grouping mode (Bay or Order Type), Then orders render in sections by the chosen grouping with the chosen sort preserved within each section. And the last used sort, filter, and grouping settings persist across page reloads for at least 7 days on the same device.
Inline Metadata Display
Given an order is visible, Then its row includes brand/app name or logo (with accessible alt text), current driver status (e.g., En Route, Arrived, Waiting), and bay assignment if any. When driver status changes or a bay is assigned/unassigned, Then the metadata updates on the row within 1 second. If any metadata field is unavailable, Then a neutral placeholder is shown and the countdown remains visible.
Accessible Large-Format UI and Reduced Motion
Given the board renders at 1080p or higher, Then countdown text is at least 64 px, order title at least 36 px, and metadata at least 24 px, scaling responsively for larger viewports. All interactive controls are operable via keyboard (Tab/Shift+Tab/Enter/Space) with a visible focus indicator and logical tab order; no tab traps. All essential text and icons meet WCAG 2.1 AA contrast (≥ 4.5:1 for normal text). No flashing content between 3–50 Hz; non-essential animations are ≤ 200 ms and disabled when prefers-reduced-motion is enabled. Each order row exposes an accessible name that includes the order identifier, state, and time remaining for assistive technologies.
Data Freshness and Connectivity Indicators
Given the real-time connection is interrupted, When no updates are received for 5 seconds, Then a Data may be stale banner appears including the timestamp of the last update. When connectivity is restored, Then the banner clears automatically and timers resynchronize within 1 second. On initial load with 100 orders, Then the board renders a usable view within 2 seconds on a standard kiosk device. If the browser tab is backgrounded, Then timers resume in sync on focus with total drift not exceeding 500 ms.
Risk Scoring & Gentle Escalations
"As an expo lead, I want at-risk orders to be automatically surfaced with gentle alerts so that I can intervene early and prevent late pickups without overwhelming the team."
Description

Implements a rules-driven risk model that flags orders likely to miss SLA based on ETA volatility, prep progress, bay availability, driver-arrival proximity, and historical lateness patterns. Triggers low-friction escalations—row highlight, gentle banner, optional soft chime, and targeted SMS ping to the expo lead—avoiding alarm fatigue. Supports acknowledge/snooze/resolve controls, audit trails of actions taken, and configurable thresholds per brand/app to align with contractual SLAs.

Acceptance Criteria
Real-time Risk Score Recalculation Across Signals
Given an order is visible on the SLA Watch board and brand/app risk thresholds are configured When any of the following inputs changes: ETA update, prep progress update, bay availability, driver proximity, or historical lateness baseline Then the order’s risk score is recalculated within 5 seconds And the score is normalized to a 0–100 scale And the risk band is assigned using the configured thresholds for that brand/app And toggling any single input from low-risk to high-risk increases the score by at least 10 points by default (configurable)
High/Critical Risk Triggers Gentle Escalations Without Alarm Fatigue
Given an order’s risk band transitions from Medium or lower to High When the transition occurs Then the order row is highlighted within 2 seconds And a gentle banner appears with concise guidance And no sound is played unless the soft chime is enabled for the location Given an order’s risk band transitions to Critical When the transition occurs Then an SMS is sent to the assigned expo lead with order ID, app, pickup ETA, and a deep link to the board And no more than 1 SMS per location is sent within any rolling 3‑minute window And repeated transitions within a 5‑minute cooldown do not generate additional banners, chimes, or SMS And if no expo lead is configured, no SMS is sent and the banner prompts to assign a lead
Operator Controls: Acknowledge, Snooze, Resolve
Given an order is escalated (High or Critical) When a user clicks Acknowledge Then escalations are suppressed for that order for 10 minutes (configurable) without changing the live risk score And an audit entry is recorded for the acknowledgment When a user Snoozes with a selected duration (2, 5, or 10 minutes) Then no banners, chimes, or SMS are emitted for that order until the snooze expires And the order’s visual state displays a countdown for the snooze When a user Resolves an escalated order Then the row styling returns to normal and no further escalations occur unless the risk score increases by at least 20 points from the resolved score or moves to a higher band
Action and Risk Transition Audit Trail
Given audit logging is enabled by default When any of the following events occurs: risk band change, Acknowledge, Snooze, Resolve, SMS sent/delivered/failed, chime played Then an audit record is stored with fields: timestamp (UTC), order ID, app, brand, previous band, new band, risk score, event type, actor (user or system), configuration version, and relevant metadata (e.g., SMS message ID) And audit records are queryable by date range and order ID in the dashboard And audit records are retained for at least 90 days
Per-Brand/App Risk Thresholds and SLA Alignment
Given a brand/app has custom SLA and risk thresholds configured in Admin When an order from that brand/app appears on the board Then the system applies that brand/app’s configured risk band thresholds and escalation rules And default thresholds are applied to brands/apps without custom configuration And changes made in Admin take effect within 60 seconds without requiring a page reload And risk scoring uses the app’s SLA definition to normalize time-to-breach across providers
ETA Volatility, Driver Proximity, and Bay Availability Influence
Given two orders have equal time remaining to SLA breach but one has experienced three or more ETA changes of ≥2 minutes within the last 10 minutes When risk is calculated Then the volatile order’s risk score is at least 10 points higher than the stable order’s (configurable) Given an order’s driver is >1 mile away and all bays are occupied within 10 minutes of pickup ETA When bay availability remains at 0 for 2 consecutive minutes Then the order’s risk score increases by at least 15 points or transitions to a higher risk band And the banner suggests bay reprioritization to the expo
Smart Reprioritization Suggestions
"As a line supervisor, I want clear, data-backed suggestions to adjust prep order so that I can keep on-time performance high with minimal disruption to the kitchen flow."
Description

Provides actionable recommendations to reshuffle make order, stage food, or reassign bays to hit upcoming SLAs. Considers current kitchen load, cook times, hot-hold limits, and driver ETA drift to suggest the minimum viable changes. Offers one-click apply with human override and plain-language rationale (e.g., “Move Order #482 ahead by 3 minutes to protect Uber Eats SLA”). Integrates with existing ticketing to update prep sequence and with the board to reflect applied changes immediately.

Acceptance Criteria
Minimal-Change Suggestion to Protect Upcoming SLA
Given normalized SLA countdowns, current kitchen load, per-order cook times, hot-hold limits, and driver ETA drift are available When the suggestion engine evaluates the active order set due to a state change (new order, ETA update, cook-time update) Then for each order with projected SLA miss > 0 minutes, it emits at most one suggestion whose time delta is the smallest among all feasible options and reduces the miss to <= 0 minutes And no suggestion may create a new projected SLA miss > 0 minutes for any other order And if no feasible option exists, no suggestion is emitted for that order
One-Click Apply Updates Prep Sequence and Board
Given a displayed suggestion that adjusts make order, staging, or bay assignment When the operator clicks Apply Then the system updates the prep sequence in the integrated ticketing system and receives a 2xx response within 2 seconds And the board reflects the new sequence/bay within 1 second after successful update And the suggestion is marked Applied with operator ID and timestamp
Human Override With Plain-Language Rationale
Given a suggestion is visible on the board Then a rationale is presented containing order ID, app/brand, target SLA timestamp, proposed action (make/stage/bay), and time delta in minutes When the operator clicks Reject Then no updates are sent to ticketing or the board and the suggestion is marked Dismissed with optional reason captured
Respect Hot-Hold Limits in Suggested Actions
Given per-item or per-order hot-hold limits are defined When generating suggestions that would advance make times Then no suggestion is produced that forecasts hot-hold duration exceeding the limit at expected pickup And if all feasible actions violate hot-hold limits, only non-make-time actions (e.g., staging or bay reassignment) may be suggested, or the order is marked Not Suggested with reason Hot-hold limit
Bay Reassignment Suggestion Without Conflicts
Given current bay occupancy and upcoming arrival ETAs are known When an order’s current bay assignment risks an SLA miss or conflict Then the engine may suggest reassignment to an available bay that preserves on-time pickup And applying the suggestion allocates the new bay, frees the old bay, and prevents double-booking with at least a 1-minute buffer between assignments And the board immediately displays the new bay for the order
Post-Apply Outcome Tracking and Attribution
Given a suggestion has been applied When the order is completed Then the system records whether SLA was met and attributes the outcome to the applied suggestion ID And brand/app on-time metrics are updated within 5 minutes And the suggestion outcome is labeled Effective if on-time, otherwise Ineffective
Failure Handling and Idempotency on Apply
Given the operator clicks Apply and the ticketing API times out or returns non-2xx Then the board state remains unchanged, no local reorder is persisted, and an error message with Retry is shown And retries are limited to 3 with exponential backoff And repeated Apply actions for the same suggestion are ignored via an idempotency key to prevent duplicate changes
SLA Configuration & Brand/App Mapping
"As an operations manager, I want to configure SLA rules by brand and app so that our board reflects the correct expectations and escalations across all locations."
Description

Introduces an admin interface to define and version SLA rules per brand/app: prep buffers, grace windows, escalation thresholds, color-state boundaries, and default bay policies. Supports multi-location overrides, effective-date scheduling, and role-based access. Ensures consistent mapping of each app’s semantics (e.g., countdown to ready vs. driver arrival) into the normalized model, with test mode and sample payloads to validate configurations before they go live.

Acceptance Criteria
Versioned SLA Rule Sets per Brand/App
Given I am an Ops Admin user When I create a new SLA ruleset for Brand "X" and App "Y" Then I can configure prep_buffer_minutes, grace_window_minutes, escalation_thresholds, color_state_boundaries, and default_bay_policy as required fields Given a ruleset draft with all required fields When I set timer_source mapping to one of ["countdown_to_ready","countdown_to_driver_arrival","countdown_to_pickup_eta"] Then the system validates mapping compatibility with the app integration and saves the draft Given a brand/app with an existing Published version effective between T1 and T2 When I attempt to publish another version with an overlapping effective window Then the publish is blocked with an error explaining the overlap Given a draft ruleset When I publish it with an effective_start in the brand’s location timezone Then an audit log entry is created capturing actor, timestamp, diff, and effective window
Multi‑Location Overrides and Precedence
Given a Published corporate ruleset for Brand "X"/App "Y" When I create a location override for locations [L1, L2] Then I can change any subset of fields, and only changed fields are stored in the override Given overrides and defaults exist When the engine resolves a value Then precedence is location_override > brand_app_default > global_default Given a location has a scheduled future version When I attempt to publish an override effective in an overlapping window Then I am prompted to merge or change dates; publish is blocked until resolved Given an override is published When I view impacted locations Then the UI shows an impact list and the API returns the list via GET /rulesets/:id/impacts
Effective‑Date Scheduling and Cutover Behavior
Given a ruleset is Published with effective_start=FutureTime When the current time reaches FutureTime Then the ruleset activates automatically without manual action Given orders are in‑flight at cutover When the new ruleset activates Then existing orders continue under the prior ruleset and new orders use the new ruleset Given a future‑dated ruleset When I attempt to backdate earlier than now minus 5 minutes Then the system blocks the change and explains the constraint Given locations in different timezones When I schedule one effective_start Then the cutover occurs in each location’s local timezone
Role‑Based Access Control for SLA Admin
Given my role is Viewer When I access the SLA admin Then I can read rulesets and download sample payloads but cannot create, edit, or publish (actions return 403) Given my role is Editor When I create or edit drafts Then I can save drafts but cannot publish or schedule; publish attempts return 403 Given my role is Publisher When I publish or schedule a ruleset Then the action succeeds and is audit‑logged with actor, timestamp, version, and diff Given my role is Auditor When I view audit history Then I can filter by brand, app, location, actor, and date, and export as CSV
Test Mode Simulation with Sample Payloads
Given a draft or published ruleset When I enter Test Mode for Brand "X"/App "Y"/Location "L" Then the UI is clearly labeled "Test Mode", production notifications are suppressed, and test sessions auto‑expire after 60 minutes Given Test Mode is active When I paste or upload sample payloads (JSON) from the selected app Then the system validates schema, applies the current mapping, and shows normalized timer, color state, and escalation timeline Given a test result is inconsistent with expectations When I change mapping or thresholds in the draft Then rerunning the sample shows the updated outcomes without affecting production orders Given I generate a shareable test link When another authorized user opens it Then access requires authentication and the link expires per policy
Mapping Consistency and Linting Across Apps
Given an app provides fields [ready_by_time, driver_eta] When I configure mapping Then I must select exactly one timer_source; conflicting selections are blocked with a validation error Given mapping references unknown or deprecated fields When I attempt to save Then linter errors identify the fields and suggest valid alternatives Given an app updates its API version When I update the integration profile Then regression checks run against stored sample payloads and must pass before publish Given a ruleset mapping is changed When I publish Then a compatibility report is generated, and publish is blocked if any active location would lose required semantics
Color States, Escalations, and Notifications
Given I configure color_state_boundaries in minutes for Green, Yellow, Orange, Red When I save Then boundaries must be non‑overlapping, contiguous from <=0 to >=max, and validated as integers Given escalations are defined (e.g., Expo ping at Yellow+5m, Manager alert at Red) When timers cross thresholds in production Then the system triggers the configured escalation targets once per threshold per order and records events in the timeline Given grace_window_minutes is set When an order is late within the grace window Then the state remains "At‑risk (grace)" and ratings‑protection annotations are applied Given default_bay_policy is defined When a pickup order enters the lot Then the bay assignment follows the policy; conflicts produce deterministic tie‑breaks and are logged
On-Time Performance Analytics
"As an owner-operator, I want clear on-time performance insights by delivery partner so that I can identify issues, adjust processes, and maintain high ratings."
Description

Tracks SLA adherence by brand/app, lateness distribution, pickup dwell times, and impact of escalations on outcomes. Provides store- and brand-level dashboards with trend lines, exception lists, and drill-down to order detail. Supports CSV export, daily/weekly email digests, and shareable read-only links for franchise owners. Annotates metric shifts with configuration changes and operational notes to enable root-cause analysis and protect marketplace ratings.

Acceptance Criteria
Store-Level SLA Adherence by Brand/App
Given normalized SLA rules exist per brand/app and the user selects a Store, Date Range, and local Timezone When the On-Time Performance dashboard loads the SLA Adherence view Then for each brand/app the UI displays on-time %, SLA-eligible count, total count, and the SLA definition label And on-time % equals round((on_time_count / sla_eligible_count) * 100, 1) and matches the backend API response And totals across brands/apps equal the overall summary shown And the trend line shows one point per local day; days with zero orders display 0% with a hollow marker And toggling a brand/app in the legend hides its series and recomputes the overall summary to exclude hidden series And when no orders meet the filter criteria, the view shows a No Data state instead of 0%
Lateness Distribution and Pickup Dwell Time Drill-Down
Given orders in the selected range have timestamps for ready_at, driver_arrived_at, pickup_at, and normalized_sla_target_by When the user opens the Lateness & Dwell tab Then the histogram displays lateness minute buckets [-15,-10),[-10,-5),[-5,0),[0,5),[5,10),…,[60,+) And lateness_min for an order equals (pickup_at - normalized_sla_target_by) in minutes And dwell_min equals (coalesce(driver_departed_at, pickup_at) - driver_arrived_at); if driver_arrived_at is null, dwell_min equals (pickup_at - ready_at) And percentile badges show P50, P90, and Average for both lateness_min and dwell_min computed from the filtered set And clicking a bucket opens an order list filtered to that bucket with columns: order_id, brand/app, promised_time, pickup_at, lateness_min, dwell_min And all computations use the store’s local timezone and values display with 1 decimal place
Exception Lists with Order Detail Drill-Through
Given the user opens the Exceptions tab with Date Range and Brand/App filters applied When the user selects Late Pickups Then a table lists the top 50 late orders sorted by lateness_min descending with columns: order_id, brand/app, promised_time, pickup_at, lateness_min, bay, escalation_count, customer_name (masked) And selecting Long Dwell lists orders with dwell_min >= the configured threshold (default 10 minutes) sorted by dwell_min descending And clicking any row opens an Order Detail modal showing timeline events (ready_at, driver_arrived_at, escalations with timestamps, pickup_at), bay assignments, notes, and late reason if captured And the modal includes a link to open the full order page in a new tab and preserves current filters on return
Impact of Escalations on On-Time Outcomes
Given SLA Watch escalations are enabled and escalation events are recorded for orders in the selected range When the user opens the Escalation Impact view and selects a brand/app and date range Then the UI displays on-time % for escalated and non-escalated cohorts, sample sizes (N), and uplift_pp = on_time_escalated% - on_time_non_escalated% And cohorts are matched by store, brand/app, day-of-week, and hour-of-day using 1:1 nearest-neighbor on ready_at within 60 minutes; unmatched orders are excluded and counts are shown And if N per cohort < 30, the uplift displays with a Low Sample badge and no confidence metric And clicking View Cohorts opens the underlying order lists for each cohort with filters pre-applied
CSV Export and Shareable Read-Only Links
Given the user has applied filters (store(s), brand/app(s), date range, view) When the user clicks Export CSV Then a CSV is generated for the current view and filters with a header row and view-appropriate columns; timestamps are ISO-8601 in the store’s timezone; numeric units are minutes; filename = curbping_otp_{view}_{YYYYMMDD}_{store}.csv And if export row count <= 50,000 the download starts within 30 seconds; otherwise the user sees Preparing export and receives an email link within 10 minutes And the exported row count equals the on-screen count for the same filters When the user clicks Share Read-Only Then a tokenized URL is created that preserves current filters and view, expires in 30 days by default (configurable), can be revoked, and is audit logged with creator and timestamp And opening the share link in a private session shows the dashboard in read-only mode with no ability to edit settings, create annotations, or export CSV
Daily/Weekly Email Digests
Given a store has at least one active digest subscription with frequency Daily or Weekly and a delivery time set in the store’s timezone When the scheduled time occurs Then each subscriber receives an email within 10 minutes containing: date range covered, on-time % by brand/app, trend sparkline (last 7 days or last 4 weeks), top 5 exceptions, and a deep link to the dashboard with filters applied And the email subject follows: [CurbPing] On-Time Performance – {Store Name} – {Date Range} And unsubscribe and manage preferences links are present and functional And delivery outcomes (delivered, bounced) are logged; hard bounces automatically disable that subscription
Annotations of Metric Shifts with Config/Operational Notes
Given configuration changes (e.g., bay count, SLA normalization settings) and operational notes can be captured with timestamps, author, and optional tags When a metric trend has a day-over-day change exceeding the configured threshold (default 10 percentage points) or when a config change/note is recorded Then the chart displays an annotation marker at the relevant timestamp with a tooltip showing the note/config item, author, and timestamp And clicking the marker opens a sidebar with full note details and links to related orders and configuration diffs And annotations are included in exports (as a separate annotations CSV) and are visible in read-only shared links And users with Analyst or higher roles can add, edit, or delete annotations; all changes are audit logged

DoorFlow Zones

Maps brands and apps to dedicated pickup doors, shelves, or staging lanes and auto‑routes arrivals accordingly. Visual lane indicators guide runners and drivers to the right spot the first time, reducing cross‑traffic, noise, and bottlenecks in shared corridors.

Requirements

Zone Configuration Manager
"As an operator, I want to define doors, shelves, and lanes and map brands and order sources to them so that arrivals are auto-routed to the correct pickup point."
Description

A unified UI and backend model to create, edit, and manage pickup zones per location—doors, shelves, and staging lanes—with attributes such as name, type, capacity, operating hours, signage label, color code, and pickup instructions. Supports mapping brands and order sources (apps/channels) to primary and fallback zones with rule specificity (brand + channel + service type + time window). Enables multi-location templates and inheritance, real-time enable/disable toggles, validation to prevent conflicting assignments, and canonical Zone IDs for event tracking and integrations.

Acceptance Criteria
Create Zone With Required Attributes and Canonical ID
Given I am a location admin on Zone Configuration Manager When I create a zone with name, type (door|shelf|lane), capacity (>=1), operating hours (with location timezone), signage label, color code (#RRGGBB), and pickup instructions Then the zone is saved and appears in the location’s zone list with a unique canonical Zone ID Given any required field is missing or invalid (empty name, unsupported type, capacity < 1, invalid hex color, malformed or overlapping hours) When I attempt to save Then the form blocks submission and displays inline errors identifying each invalid field Given an existing zone When I update any editable attribute except Zone ID (e.g., name, capacity, hours, color, instructions) Then the changes are persisted, reflected in the UI within 1 second, and the Zone ID remains unchanged
Enable/Disable Zone Real-Time Routing Control
Given a zone is enabled and part of an active routing rule When I toggle the zone to Disabled Then new arrivals matching that rule stop routing to this zone within 2 seconds and are routed per eligible fallback or next rule Given a zone is disabled When I toggle it to Enabled Then it becomes eligible for routing within 2 seconds and the UI indicator shows Enabled Given arrivals already assigned to the zone before it was disabled When the zone is disabled Then their assignment remains unchanged while subsequent arrivals are not assigned to the disabled zone
Routing Rule Specificity and Precedence
Given multiple routing rules exist for a location When an arrival includes brand, channel, service type, and timestamp Then the system selects the most specific matching rule in this order: brand+channel+service type+time window > brand+channel+service type > brand+channel > brand > default Given the selected rule’s target zone is enabled and within its operating hours When the arrival is processed Then the arrival is auto-routed to that zone and the assigned zone is displayed in the runner UI Given the selected rule’s target zone is out-of-hours, disabled, or at capacity When the arrival is processed Then the system skips that rule and evaluates the next eligible rule or configured fallback
Fallback Routing on Unavailable or Full Zones
Given a routing rule defines a primary zone and an ordered list of fallback zones When the primary zone is disabled, out-of-hours, or at capacity Then the arrival is routed to the first eligible fallback zone Given all fallback zones are ineligible When an arrival matches the rule Then the system assigns the default zone if configured; otherwise flags the arrival for manual assignment and records a routing_failed event Given the primary zone becomes eligible again When subsequent arrivals occur Then routing resumes to the primary zone according to the rule
Conflicting Assignment Validation
Given I attempt to save two rules of equal specificity (same brand, channel, service type) with overlapping time windows that target different zones When I save the rules Then the system blocks the change and displays a conflict error listing the conflicting rules and fields Given a rule’s time window overlaps an existing rule’s window for the same match keys When I adjust the start or end time Then the UI provides real-time validation indicating whether the conflict is resolved before allowing save Given I modify specificity, time window, or target zone to remove overlap When I save Then the rules persist successfully and no conflicts are reported
Multi-Location Templates and Inheritance
Given I create a zone template with attributes and routing rules When I apply it to multiple locations Then zones and rules are created at each location with inherited attributes and unique Zone IDs per location Given a location overrides an inherited attribute (e.g., capacity, operating hours, signage label) When the template is later updated Then non-overridden attributes propagate to the location while overridden fields remain unchanged Given I detach a template from a location When I confirm the detach action Then the location retains current zones and rules as independent copies with no further inheritance linkage
Event Tracking and Integrations with Canonical Zone IDs
Given an arrival is routed to a zone When the system emits events (arrival_detected, zone_assigned, pickup_completed) Then each event payload includes canonical Zone ID, zone name, location ID, and timestamp in ISO 8601 Given a zone is renamed or its attributes are edited When subsequent events are emitted Then the canonical Zone ID in event payloads remains unchanged Given an external integration queries the Zones API by Zone ID When the ID exists for the tenant Then the API returns the correct zone regardless of name changes; otherwise it returns 404 for unknown IDs
Channel-to-Zone Routing Rules Engine
"As a dispatcher, I want arrivals auto-routed to the best zone based on brand, channel, and current conditions so that cross-traffic and bottlenecks are reduced."
Description

A deterministic, auditable rules engine that consumes arrival signals (brand, channel/app, order type, schedule, location, bay) and outputs an assigned zone plus human-readable instructions. Supports precedence hierarchy, time-of-day windows, per-location overrides, capacity-aware selection, and safe fallbacks. Integrates with existing bay assignment to avoid conflicts, provides re-route capability, logs decision trails for support, and exposes APIs/webhooks for downstream systems.

Acceptance Criteria
Deterministic Precedence Hierarchy Resolution
Given a ruleset where lower precedence_rank indicates higher priority and rule_id is a stable tiebreaker When an arrival matches multiple rules Then the engine selects the matching rule with the lowest precedence_rank And if two rules share the same precedence_rank it selects the one with the lowest rule_id consistently across runs And the same input evaluated 100 times yields the same zone_id, zone_name, and instructions_text And the output contains instructions_text <= 160 characters with no unresolved {placeholders} And decisionTrail lists all evaluated rules with outcome (matched/skipped) and reasons
Time-of-Day Windows and Schedule Handling
Given a rule with active_window 10:00–14:00 in the location_time_zone When an arrival occurs at 13:59:59 local Then the rule is eligible to match When an arrival occurs at 14:00:00 or later Then the rule does not match Given a scheduled order with pickup_time 18:30 When evaluated prior to arrival and a schedule-targeted rule exists Then eligibility is determined by pickup_time rather than current_time And decisionTrail records evaluated_at_local, evaluated_at_utc, and time_zone
Per-Location Overrides Take Priority
Given a global rule for brand X and a location override for brand X with higher priority When an arrival for brand X occurs at that location Then the location override is applied and the global rule is ignored When the same arrival occurs at a location without an override Then the global rule is applied And decisionTrail records rule_scope (global/location) and rule_ids considered
Capacity-Aware Zone Selection
Given zone Z1 has capacity 10 and current_load 10, and zone Z2 has capacity 5 and current_load < 5, and the rule preference order is Z1 then Z2 When an eligible arrival is evaluated Then the engine assigns Z2 and records capacity_exhausted for Z1 in decisionTrail When current_load for Z1 drops below capacity Then subsequent arrivals may be assigned to Z1 again And no assignment results in current_load exceeding capacity for any zone
Safe Fallback Routing on No-Match or Dependency Error
Given an arrival that matches no rules When evaluated Then the engine assigns the location default_zone and sets a human-readable fallback instructions_text And zone_id is never null Given a dependency timeout from bay assignment service When routing is attempted Then the engine returns default_zone and includes error details in decisionTrail without returning an HTTP 5xx
Bay Assignment Integration Without Conflicts
Given an arrival with pre_assigned_bay B5 and eligible zones where Z1 maps to B5 and Z2 maps to B7 When evaluated Then the engine selects Z1 and logs conflict_avoided for Z2 in decisionTrail Given a potential selection would double-book bay B5 When evaluated Then the engine selects the next compatible zone or returns the fallback zone, and no two active assignments share the same bay unless explicitly allowed by configuration
Re-route Capability, Audit Trail, and Webhook Emission
Given an initial assignment to Z1 When capacity changes or staff triggers a manual re_route to Z3 via API Then the engine re-evaluates rules, updates the assignment to Z3 if eligible, and persists both prior and new decisionTrail with timestamps and actor And reroute_count increments by 1 Given a successful routing decision When emitting integrations Then a webhook POST is sent within 500 ms containing request_id, zone_id, zone_name, instructions_text, decisionTrail, idempotency_key, and signature And duplicate deliveries with the same idempotency_key are ignored idempotently by the receiver
Arrival Instruction Messaging
"As an arriving customer, I want clear instructions telling me which door or shelf to use so that I can pick up without confusion or delays."
Description

Automated generation and delivery of clear, concise lane/door instructions to customers and staff across SMS, the mobile web arrival page, and staff consoles/print tickets. Supports visual badges (color/letter/number), concise SMS-friendly phrasing, localization/accessibility, and update notifications if re-routed. Requires no additional apps or hardware and provides signage-friendly shortcodes/QR for consistent wayfinding.

Acceptance Criteria
SMS Instruction Delivery on Arrival
Given an order is mapped to a DoorFlow Zone with a defined badge (color + letter/number) and optional bay And the customer’s phone number is SMS-capable When the customer triggers arrival via the “I’m here” web link or by texting the ARRIVE keyword to the site shortcode Then the system sends exactly one instruction SMS within 5 seconds And the SMS includes lane/door name, visual badge text (e.g., “Door C (Blue)”), and bay number if assigned And the SMS length is ≤ 160 characters including dynamic content And the SMS contains a short link to the arrival page for consistency And no app download or login is required to receive or read the SMS
Mobile Web Arrival Page Instruction Display
Given an active order with a DoorFlow Zone assignment When the arrival page is opened from the SMS link Then the page prominently displays the same lane/door, badge (color + letter/number), and bay as the SMS And the badge text alternative explicitly states the color and letter/number (e.g., “Door C, Blue”) And foreground/background contrast for badge and text meets WCAG AA (≥ 4.5:1) And key instruction text is visible within the first viewport on common devices (iPhone SE, iPhone 14, Pixel 7) And the page loads in ≤ 2 seconds on a 4G connection (400 ms TTFB, ≤ 1 MB total)
Localized Instruction Messaging (Spanish)
Given the customer’s locale preference is Spanish or the browser Accept-Language includes es When the instruction SMS is sent and the arrival page is rendered Then SMS and page copy are presented in Spanish using the approved glossary And dynamic tokens (lane/door names, letters/numbers) remain consistent across locales And if Spanish translations are unavailable, content falls back to English without placeholders And right-to-left behavior is not triggered for Spanish and layout remains intact
Staff Console and Print Ticket Synchronization
Given an order is routed to a DoorFlow Zone When the customer marks arrival Then the staff console displays lane/door, badge, and bay within 3 seconds of arrival And the print ticket (58mm and 80mm) includes the same data in a single highlighted line (≤ 32 chars) using the format: “Door C (Blue) — Bay 4” And console, ticket, and customer-facing channels show identical values within the same minute And if no bay is assigned, the format omits the bay segment without trailing punctuation
Re-route Update Notifications
Given an order has already received initial instructions And the DoorFlow Zone for the order changes due to operational re-routing When the re-route event is saved Then the arrival page updates the instruction within 2 seconds without page reload And an “Updated” SMS is sent within 5 seconds containing the new lane/door and badge And no more than one update SMS is sent per 30 seconds per order And the staff console shows the new assignment with a timestamp and “Updated” indicator And the superseded instruction is no longer visible in any channel
Signage Shortcode and QR Wayfinding
Given venue signage exposes a branded QR code and SMS shortcode When a customer scans the QR with default camera apps (iOS, Android) or texts the keyword to the shortcode Then the arrival page opens or an SMS reply returns with lane/door instructions without requiring any app install And the QR URL is ≤ 50 characters using the venue’s branded domain And the QR resolves to the arrival page in ≤ 2 seconds on 4G And SMS keyword handling works with common carriers (AT&T, Verizon, T-Mobile) and returns a response in ≤ 5 seconds And brand/app context from the QR or shortcode maps to the correct DoorFlow Zone
Runner Dashboard Lane Indicators
"As a runner, I want a clear view of which orders belong to which lanes or doors so that I can stage and deliver to the right spot on the first try."
Description

Enhancements to the runner/expo dashboard to display real-time zone indicators for each order, including color-coded labels, queue position, bay/vehicle info, and aging timers. Enables filtering by zone, batching by lane, sound/visual alerts for new arrivals, and quick-actions to mark staged, handed-off, or re-route. Web-based, low-latency updates optimized for existing tablets/monitors with offline tolerance and auto-refresh.

Acceptance Criteria
Real-Time Zone Indicator Display
- Given an order is assigned to a DoorFlow zone/lane and the runner dashboard is open, when the arrival ping is received, then a zone indicator chip appears on that order within 1 second. - The indicator shows zone name and lane code (e.g., "North Door • Lane B") with the configured zone color. - The indicator is visible and not occluded at 1024x600 and 1920x1080 in both portrait and landscape orientations. - Zone color and text meet WCAG AA contrast (≥ 4.5:1), and lane identity is also conveyed via text/icon (not color alone).
Bay & Vehicle Info Visibility
- Given bay number and vehicle descriptors are provided via the arrival link, when the order is displayed, then the bay number shows as a badge adjacent to the zone indicator. - Vehicle make/color/license render on a single line with ellipsis when needed and full value available on hover/tap tooltip. - If bay assignment changes, the badge updates within 1 second and briefly highlights for 2 seconds. - If a field is missing, a placeholder ("—") is shown and layout shift does not exceed 8 px when fields populate.
Queue Position & Aging Timer Accuracy
- Each order displays its queue position within its zone/lane, starting at 1 for the next to run. - Queue position recalculates within 500 ms after Stage, Hand-off, or Re-route actions. - Aging timer shows mm:ss since arrival, updates every second, and drift is < 2 seconds over 10 minutes. - Aging timer persists across reloads and resumes correctly after offline periods using server timestamps.
Zone/Lane Filtering & Batching
- Multi-select filter allows choosing one or more zones/lanes; list updates within 300 ms. - Only orders from selected zones/lanes are displayed; per-zone counts are shown in the filter control. - "Batch by lane" groups orders under lane headers while sorting within each group by aging (oldest first). - Clearing filters restores the full list; filter state persists for the current session across refresh.
New Arrival Alerts (Sound/Visual)
- When a new arrival is assigned to any visible zone/lane, a visual toast appears within 500 ms and stays for 5 seconds or until dismissed. - An audio chime plays once per arrival; no duplicate alerts fire on reconnection or page refresh for the same event. - Alerts respect a per-device mute toggle and do-not-disturb schedule; when muted, only visual alerts are shown. - Clicking the alert scrolls to and focuses the corresponding order row.
Quick Actions: Stage/Hand-off/Re-route
- Each order row provides Stage, Hand-off, and Re-route buttons usable via touch (≥44 px targets) and keyboard (tab/enter/space). - Stage and Hand-off update order state locally and on the server; updates appear on all connected dashboards within 1 second. - Re-route opens a zone/lane selector; upon confirmation, the order moves groups, updates its indicator, and recalculates queue position within 1 second. - All quick actions create an audit record (user, action, timestamp); on failure, an inline error is shown and state is not changed.
Offline Tolerance & Auto-Refresh
- If connectivity is lost for >2 seconds, an offline banner appears; the list remains visible and Hand-off/Re-route are disabled with clear affordances. - Actions attempted offline are queued with a badge count and auto-retried on reconnect; conflicts resolve using server timestamps with a user-visible message. - On reconnection, data reconciles within 3 seconds with no duplicate or missing orders. - When online, the dashboard maintains live updates via WebSocket/SSE with heartbeat and also performs a full data refresh at least every 60 seconds.
Capacity and Overflow Management
"As a shift lead, I want the system to manage lane capacity and overflow so that we avoid congestion and keep wait times low."
Description

Real-time tracking of per-zone occupancy and service SLAs with configurable thresholds. When a zone nears capacity or breaches SLA, the system temporarily throttles or overflows new arrivals to predefined secondary zones, alerts staff, and updates customer instructions. Includes manual override tools, pause/unpause for zones, and historical data to calibrate default capacities by daypart.

Acceptance Criteria
Real‑Time Per‑Zone Occupancy Tracking
Given a zone with defined capacity and active orders, When a new arrival is detected via the "I'm here" link, Then the zone's occupancy increases by 1 within 2 seconds. Given an occupied bay in the zone, When the order is marked served or the customer departs, Then the zone's occupancy decreases by 1 within 2 seconds and never drops below 0. Given multiple simultaneous arrivals for the same zone, When they are detected within the same second, Then each arrival is counted exactly once and occupancy reflects the total without duplicates. Given a network reconnect or page refresh, When the dashboard reloads, Then the occupancy count matches the server state within 3 seconds. Given a zone at capacity, When an additional arrival is detected and no override exists, Then the arrival is not assigned to the zone and a routing decision is triggered.
SLA Breach Detection and Auto‑Overflow
Given a zone with an SLA threshold configured in minutes, When any active arrival's wait time reaches or exceeds the threshold, Then the zone state changes to "SLA Breached" within 2 seconds. Given a zone in "SLA Breached" state, When a new arrival is detected, Then the arrival is auto-assigned to the first available secondary zone per the configured priority list within 3 seconds. Given a zone in "SLA Breached" state, When no active arrival has exceeded the SLA for 3 consecutive minutes, Then the zone state returns to "Normal" and new arrivals are routed per primary rules. Given no configured secondary zones, When an SLA breach occurs, Then the system prevents new assignments to the zone and presents a "temporarily full" message with retry guidance.
Near‑Capacity Throttling and Secondary Zone Assignment
Given a zone with a near-capacity threshold (e.g., 85% of capacity), When occupancy meets or exceeds the threshold, Then the zone state changes to "Near Capacity" within 2 seconds. Given a zone in "Near Capacity" and overflow mode enabled, When a new arrival is detected, Then the arrival is assigned to the next eligible secondary zone per priority within 3 seconds. Given a zone in "Near Capacity" and throttle mode enabled, When a new arrival is detected, Then the ETA/instruction advises a delayed check-in by a configurable number of minutes without assigning a bay. Given all secondary zones are unavailable, When overflow is required, Then the system surfaces a "temporarily full" instruction and does not assign a primary zone. Given occupancy drops below the near-capacity threshold for 2 consecutive minutes, When a new arrival is detected, Then routing returns to the primary zone.
Staff Alerts and Visual Indicators
Given any zone enters "Near Capacity" or "SLA Breached", When the state changes, Then a color-coded banner appears on the dashboard and an audible alert plays within 2 seconds. Given alert subscribers are configured, When "SLA Breached" is entered, Then an SMS alert is sent to the on-duty lead within 5 seconds with zone name, reason, and timestamp. Given a zone recovers to "Normal", When recovery criteria are met, Then the banner clears and a recovery notice is logged without sending duplicate alerts. Given repeated state flaps within 60 seconds, When alerts would otherwise fire, Then alerts are rate-limited to one per state type per 60 seconds.
Customer Instruction Updates on Overflow
Given a customer taps the "I'm here" link for a brand mapped to a primary zone, When that zone is overflowed or throttled at the time of arrival, Then the customer is presented with updated instructions for the assigned secondary zone within 2 seconds. Given updated instructions are sent, When the customer has already received a prior pickup instruction, Then only the latest valid instruction is visible and any conflicting prior message is suppressed. Given lane indicators are configured, When a customer is overflow-assigned, Then the instructions include the correct lane/bay number and door description matching the secondary zone mapping. Given no secondary zone is available, When the customer taps "I'm here", Then the customer receives a clear "temporarily full" message with retry guidance and an optional estimated delay.
Manual Override and Zone Pause/Unpause
Given a staff user with appropriate permissions, When they apply a manual override to assign an arrival to a specific zone, Then the arrival is assigned as requested within 2 seconds and the action is logged with user, time, and reason. Given a zone is paused, When any new arrival is detected for that zone, Then the system does not assign it to the paused zone and follows overflow/throttle rules. Given a paused zone is unpaused, When new arrivals occur, Then routing resumes to the zone per normal rules. Given a manual override is set with an expiry (e.g., next 2 arrivals or 15 minutes), When the expiry condition is met, Then the system automatically reverts to standard routing. Given audit requirements, When any pause, unpause, or override occurs, Then an immutable log entry is created with before/after states.
Historical Data and Daypart Capacity Calibration
Given zones generate occupancy and SLA data, When the system aggregates metrics, Then it stores per-zone, per-15-minute interval occupancy, arrivals, and SLA breach counts for at least 90 days. Given sufficient history for a daypart (e.g., lunch 11:00–14:00), When calibration is requested, Then the system proposes default capacity values per zone based on the 95th percentile occupancy while maintaining SLA under target. Given a proposed calibration is reviewed, When an authorized user accepts it, Then the new default capacities are applied to the zone configuration effective on the specified date/time. Given data gaps exist for a daypart, When calibration is requested, Then the system flags insufficient data rather than generating a proposal.
Zone Performance Analytics
"As an operator, I want analytics by zone so that I can optimize staffing, signage, and lane assignments."
Description

Dashboards and reports that surface zone-level metrics such as arrivals, average time-to-handoff, dwell time, re-route rate, SLA adherence, and brand/channel mix. Enables comparisons by time of day and across locations, exports to CSV, and integrates with existing CurbPing analytics. Provides insights and recommendations to optimize staffing, signage, and zone configurations.

Acceptance Criteria
View zone-level KPIs dashboard
- Given I open Zone Performance Analytics for a selected location and date range, Then I see per-zone KPIs: Arrivals, Avg Time-to-Handoff, Avg Dwell, Re-route Rate, SLA Adherence %, Brand/Channel Mix - KPI values, units, and definitions match the analytics glossary (minutes for time, % for rates, counts for arrivals) - A data freshness indicator is visible; for live ranges it is ≤5 minutes old; for historical ranges it indicates data complete through range end - Filters (date range, location, brand, channel, zone) apply to all widgets; 95% of filtered queries return within 3 seconds for ≤30 days of data; UI updates within 1 second after data returns - Totals reconcile: sum of per-zone arrivals equals total arrivals for the applied filters (±0 due to rounding)
Compare zones by time of day and across locations
- Given two or more zones are selected and a time grain (15-min, hour, daypart) is chosen, When Compare is applied, Then charts and tables render side-by-side with aligned time axes - Cross-location comparisons use each location’s local timezone; merged views convert to the report timezone selected by the user - Dayparts are configurable; when definitions change, metrics recompute within 60 seconds and reflect in all compare views - Users can sort by any KPI ascending/descending; sort order persists when filters change within the same session - Shareable links preserve compare selections (zones, locations, time grain, date range) via URL parameters
Export zone metrics to CSV
- Given current filters and visible table, When Export CSV is clicked, Then a file downloads within 5 seconds containing a header row with standardized field names - File format: UTF-8, comma-delimited, LF newlines, double-quote text qualifier; no BOM; numeric fields unquoted - Row granularity: one row per zone per selected time grain; required columns include zone_id, zone_name, location_id, location_timezone, window_start, window_end, arrivals, avg_time_to_handoff_seconds, avg_dwell_seconds, reroute_rate_pct, sla_adherence_pct, brand, channel - Export respects all active filters and compare selections; totals match on-screen values within rounding tolerance - For exports expected to exceed 100k rows, an async export flow is offered with job status, email link on completion, and 7-day expiry for download
Integrate with existing CurbPing analytics
- Zone Performance appears under Analytics navigation and inherits SSO and RBAC; only users with Analytics.View can access; ZoneAdmin or higher can export - Deep links accept query parameters for filters and open the module with those filters applied - A public (authenticated) API endpoint /v1/analytics/zones returns the same metrics and supports pagination, filtering (date range, location, zone, brand, channel), and responds within 10 seconds for 30 days across 100 zones - Metric names and calculations are identical to existing analytics taxonomy; regression tests confirm parity against baseline datasets - Access and export events are written to audit logs with user, timestamp, and filter summary
Generate insights and recommendations for zone optimization
- Recommendations trigger when thresholds are met (e.g., SLA Adherence < 90% for 3 consecutive hours, Re-route Rate > 10% over the last 7 days) - Each recommendation includes affected zone(s), KPI(s) driving the alert, suggested action (e.g., add runner 11:00–13:00, update signage for Door B), expected impact range, and a confidence score (0–1) - Recommendations are time-bounded and auto-expire after metrics recover to target for 48 consecutive hours - Users can Dismiss, Snooze, or Accept; actions are tracked and exposed in analytics; Accept emits a task webhook/event with payload details - An explanation pane lists the top 3 contributing factors (e.g., brand/channel mix shift, arrival surges, dwell outliers) with supporting data points
Data quality and SLA adherence calculations
- SLA adherence is defined as the % of arrivals with handoff_time - arrival_time ≤ configured SLA seconds; configuration can be set per brand/channel/zone with a global default fallback - Dwell time = handoff_time - arrival_time; when the “Trim outliers” toggle is ON, values above the P99 per zone are excluded from averages; when OFF, all values included - Re-route is recorded when a zone assignment changes within 3 minutes of initial assignment; reroute_rate_pct = rerouted_arrivals / total_arrivals for the period - Orders with missing handoff after 2 hours are counted as SLA failures and excluded from Avg Time-to-Handoff; such exclusions are surfaced in a data quality banner with counts - A data completeness monitor displays ingestion lag and missing event counts per source; alerts fire when lag > 10 minutes for >5 consecutive minutes
Role-Based Access and Audit Trail
"As an owner, I want only authorized staff to change DoorFlow settings and to see who changed what so that operations remain consistent across shifts."
Description

Granular permissions for creating, editing, and publishing zones and routing rules, scoped by role and location/brand. Every change is logged with who/what/when, diff view, and rollback to prior versions to safeguard operations across shifts. Supports approval workflows for multi-brand operators.

Acceptance Criteria
RBAC: Scoped permissions for zones and routing rules
- Given a user with role=Zone Editor scoped to Brand A, Location 1, When they create or edit zones or routing rules within that scope, Then the action succeeds and is saved as a draft with a 201/200 response. - Given the same user, When they attempt to create/edit/publish for any out-of-scope brand/location, Then the UI controls are disabled and any API call returns 403 RBAC_SCOPE_VIOLATION and an audit entry outcome=denied reason=scope is recorded. - Given a user without publish permission, When they attempt to publish, Then the publish control is hidden and direct API publish returns 403 RBAC_PERMISSION_MISSING with an audit entry outcome=denied reason=permission. - Given a Super Admin, When accessing any brand/location, Then all CRUD actions on zones and routing rules are permitted and logged with outcome=success.
Approval workflow: Draft, submit, approve/reject, publish
- Given a Zone Editor with draft changes, When they submit for approval, Then the draft status updates to Pending Approval, approvers for the brand/location are notified, and an audit entry action=submit_for_approval is recorded. - Given an Approver, When they approve a pending draft, Then the changes publish atomically within 2 seconds, the status updates to Published, and an audit entry action=approve includes approverId and timestampUTC. - Given an Approver, When they reject a pending draft with a required comment (min 5 characters), Then the draft status updates to Rejected, no publish occurs, the comment is stored and visible to the submitter, and an audit entry action=reject includes comment. - Given a draft, When all required approvals are configured as multi-step (if enabled), Then publishing only occurs after all steps approve; partial approvals do not publish.
Audit trail: Comprehensive event logging for changes
- Given any create/edit/delete/publish/approve/reject/rollback action on zones or routing rules, Then an audit event is recorded with fields: actorId, actorRole, actorScope, actionType, entityType, entityId, entityName, brandId, locationId, timestampUTC (ISO 8601), clientIp, userAgent, outcome (success/denied/error), correlationId. - Given an edit action, Then before and after field values are captured at field-level granularity; sensitive fields are masked per policy (e.g., tokens -> ****). - Given an action that errors, Then an audit event outcome=error is recorded with errorCode and errorMessage. - Given audit search via UI or API, When filtering by date range, actor, actionType, brand/location, entity, Then results return within 2 seconds for up to 50,000 records and are sortable by timestamp descending.
Diff view: Accurate, readable comparison of versions
- Given an audit event for an edit, When viewing the diff, Then added/removed/modified fields are highlighted; whitespace-only changes are ignored. - Given nested objects/lists (e.g., lane assignments, routing rules), When viewing the diff, Then a hierarchical diff renders with clear indicators for adds/removes/edits and a toggle "Show only changes" filters unchanged fields. - Given the stored before/after snapshots, When computing the diff, Then the diff corresponds exactly to the JSON delta between snapshots (excluding masked fields) and reflects array reorders distinctly from content changes.
Rollback: Restore prior versions safely
- Given a user with rollback permission, When they select Restore on a prior version, Then a new draft is created from that version with sourceVersionId recorded; no publish occurs without required approvals. - Given an Approver selects Restore and Publish, When executed, Then the selected prior version becomes the active published version within 3 seconds, and an audit entry action=rollback includes sourceVersionId and targetVersionId. - Given current validation rules, When a rollback would violate validation (e.g., invalid bay numbers), Then the rollback is blocked with error VALIDATION_FAILED and a list of invalid fields; no changes are applied.
Scoped visibility and export of audit logs
- Given a user scoped to Brand A, Location 1, When they view audit logs, Then only events within that brand/location are visible; cross-brand/location events are not returned by UI or API. - Given a Super Admin, When viewing audit logs, Then events across all brands/locations are visible and filterable by scope. - Given an export request (CSV or JSON) for a filtered result set up to 100,000 events, Then the export completes within 30 seconds, includes the full audit schema, is available for download for 7 days, and the export itself is logged with actor and filter criteria.

PingBridge

Ingests order events from delivery apps and POS (email/API/webhook) and de‑duplicates them into a single live tile. Keeps every ping in one place with reliable state transitions—‘Arriving,’ ‘At Door,’ ‘Handed Off’—so nothing gets missed across brands.

Requirements

Omnichannel Order Ingestion Adapters
"As an operations manager, I want to connect our delivery apps and POS to PingBridge so that orders automatically appear without manual entry across all brands and locations."
Description

Build connectors to ingest order lifecycle events from delivery apps and POS via email parsing, REST APIs, and webhooks. Support common providers (e.g., DoorDash, Uber Eats, Toast, Square) with configurable authentication, signature verification, and inbox routing. Normalize all inbound payloads (order created/updated/ready/out-for-delivery/arrived/canceled) into a canonical schema with location, brand, customer, ETA, and pickup instructions. Handle HTML/plain-text email templates and attachments, tolerate minor format drift, and enrich records with source metadata for traceability. Ensure near–real-time processing and resilience to transient provider outages.

Acceptance Criteria
Signed Webhook Ingestion and Normalization (DoorDash/Uber Eats)
Given a configured provider DoorDash or Uber Eats with valid webhook signing secret When a POST request with event order.created, order.updated, order.ready, order.out_for_delivery, order.arrived, or order.canceled is received at /webhooks/{provider} with headers per provider spec Then the signature is validated per provider HMAC algorithm and the request is accepted with HTTP 202 within 500 ms And the payload is normalized into the canonical schema with fields: provider, provider_event_id, provider_event_type, order_id, location_id, brand_id, customer, eta, pickup_instructions, state, items, totals, received_at And the canonical event is persisted and available to downstream consumers within 5 seconds of receipt And duplicate delivery of the same provider_event_id is acknowledged with 202 and results in no duplicate canonical records or state regression
Email Ingestion for HTML/Plain-Text Templates and Attachments (Toast/Square)
Given an ingestion mailbox is configured for a Toast or Square location alias When an email matching the provider FROM domain and subject patterns is received with HTML and/or plain-text body, optionally with PDF/PNG attachments Then the parser extracts order_id, location_id, brand_id, customer name and phone, ETA (with timezone), pickup instructions, state, items, and totals from the body; if any field is missing in body, attachments are parsed as fallback And the parser correctly normalizes the event type (created/updated/ready/out_for_delivery/arrived/canceled) according to the email content And designated non-breaking format drifts (added banners, extra whitespace, section re-ordering, new marketing footer) in the provided drift corpus are tolerated with 100% field extraction success And the canonical event is available to downstream within 10 seconds of email receipt And emails failing provider pattern checks are quarantined with reason code and are not processed
Cross-Channel De-duplication into Single Live Order
Given the same merchant order is received from multiple channels (e.g., webhook and email) within a 30-minute window When canonicalization runs for each inbound event Then events are unified into a single canonical order using idempotency keys composed of provider_order_id or store_order_number plus location_id and brand_id And only one canonical order record exists and subsequent events update its state without creating duplicates And state transitions are monotonic and valid against the transition map; invalid or regressive transitions are rejected and logged And downstream receives a single sequence of state changes without duplicate notifications
Configurable Authentication and Routing per Provider and Location
Given credentials (API keys, client secrets, webhook signing secrets) and routing rules are configured for DoorDash, Uber Eats, Toast, and Square per location When inbound events arrive via webhook, API pull, or email alias Then authentication and signature verification use the configured secrets and fail closed with HTTP 401/403 on invalid credentials or signature And events are routed to the correct brand and location context based on configured identifiers in the payload, headers, or mailbox alias And rotating a signing secret supports a primary and secondary secret for a 24-hour overlap with zero downtime and no false negatives And all auth and routing actions are audited with timestamp, actor, and outcome
Resilience, Retry, and Backlog Recovery for Transient Outages
Given a provider API or mailbox experiences transient failures (network errors, HTTP 429, or 5xx) When ingestion attempts fail Then the adapter retries with exponential backoff and jitter for up to 24 hours, respecting provider rate limits And events are processed with at-least-once delivery semantics and deduplicated via idempotency keys And upon provider recovery, the backlog is drained and end-to-end lag returns below 60 seconds within 5 minutes And no events are lost; dead-lettered events include actionable error codes and raw payload references
Canonical Schema Validation and Source Traceability
Given any inbound event from supported providers When the event is normalized Then the canonical schema includes required fields: provider, provider_event_type, provider_event_id, source_channel (email/webhook/api), signature_validated, received_at, location_id, brand_id, order_id, customer.name, customer.phone, eta.iso8601, pickup_instructions, state, items[], monetary totals, and raw_payload_reference And events missing required fields are rejected to a dead-letter queue with a specific validation error and are not forwarded downstream And the raw source payload is stored immutably for 30 days and is retrievable by provider_event_id for audit And 100% of accepted events pass JSON schema validation in CI and runtime validation metrics show <0.1% schema errors over a rolling 7 days
De-duplication and Reconciliation Engine
"As a curbside lead, I want duplicate order notifications combined into one live tile so that staff aren’t confused and no customer gets missed."
Description

Implement a matching engine that merges duplicate orders and events arriving from multiple sources into a single canonical order tile. Use deterministic keys when available (provider order IDs) and fuzzy heuristics (timestamp windows, customer name/phone, total, items, store) when not. Maintain source-to-canonical linkage, ensure idempotent updates, and resolve conflicts with precedence rules and event versioning. Continuously reconcile late or out-of-order events, backfill missing fields, and surface merge/audit history for support. Expose confidence scores and guardrails to prevent accidental merges across distinct orders.

Acceptance Criteria
Deterministic Merge via Provider Order ID
Given multiple inbound order payloads share the same provider_order_id and store_id And the provider_order_id is non-empty and matches exactly (case-insensitive) When the payloads are ingested within a 24-hour business day window Then exactly one canonical order tile is created per unique (store_id, provider_order_id) And all source payloads are linked to that canonical order with source metadata preserved And subsequent duplicate payloads update the canonical idempotently (no duplicate events, no additional tiles) And retrying the same payload N times (N ≥ 3) yields the same canonical state and version number And state transitions apply once in order of event_version; out-of-order lower versions are ignored but audited
Fuzzy Merge Without Deterministic Keys
Given two or more inbound orders lack a shared deterministic key And they match on store_id AND customer_phone normalized OR customer_name normalized And their order_totals differ by ≤ $1.00 OR item sets overlap by ≥ 70% And their created_at timestamps are within a 10-minute window When the computed confidence score ≥ configurable threshold T (default 0.90) Then the engine auto-merges them into one canonical order and records confidence, features, and matched fields And if 0.60 ≤ confidence < T, the orders remain separate, flagged as "Possible Duplicate" for review And if confidence < 0.60, no merge suggestion is recorded And all merge decisions are written to the audit log with rationale and feature weights
Out-of-Order and Late Event Reconciliation
Given events for a canonical order arrive out of chronological order (e.g., At Door before Arriving) And each event includes an event_version or a provider timestamp When events are applied Then the engine enforces monotonic state progression using precedence rules and versioning And rejects regressions unless a higher version explicitly supersedes with a valid reason code And late events older than 2 hours are applied only if they add missing fields; otherwise they are logged as ignored And the final visible state reflects the highest-precedence, highest-version event And the audit history lists the received order, applied/ignored status, and reasons
Conflict Resolution and Field Precedence
Given multiple sources provide conflicting values for the same fields on a canonical order When conflicts occur for totals, items, or customer contact Then precedence is applied per configurable policy: POS > API webhook > email parse > manual And within the same source, higher event_version supersedes lower And all superseded field values are retained in audit with timestamps and source And a reconciliation summary is available showing current value, losing values, and reasons And no data is dropped without an audit entry
Source Linkage and Merge/Audit History Exposure
Given a canonical order exists When a support user queries by canonical_id or any source message_id Then the system returns the canonical record plus the list of all linked source records And the merge history shows when and why each source was linked, confidence scores, and the decision path And the API returns audit entries with pagination, filterable by time range, source, and action (merged, ignored, superseded) And exporting the audit to CSV/JSON reproduces the on-screen data with identical counts
Guardrails Against Cross-Order Accidental Merge
Given inbound orders originate from different store_ids OR different business dates When evaluated for merging Then the engine must not merge them regardless of confidence score And orders with mismatched customer_phone country codes or total variance > $5.00 are not auto-merged And any manual merge requires explicit confirmation and records the approver and reason And an unmerge operation restores pre-merge source records and recomputes the canonical state deterministically And false-merge rate in test set ≤ 0.1% with T=0.90
Idempotent Reprocessing and Backfill
Given the ingestion pipeline is re-run with the same batch of payloads (replay) When the engine reprocesses the batch Then the set of canonical IDs and their final states remain unchanged (no duplicates created) And backfilled fields (e.g., customer_phone from later source) are added without altering unrelated fields And version numbers increment only when a material field or state changes And checksum of canonical snapshot before/after replay matches except for allowed backfills And performance: reprocessing 1,000 historical payloads completes within 60 seconds on staging baseline
Unified Order State Machine
"As a front-of-house team member, I want consistent, reliable status meanings across all providers so that I always know the next action to take."
Description

Create a robust, auditable state machine that maps provider-specific statuses to a unified lifecycle: New → Preparing → Arriving → At Door → Handed Off → Completed, with branches for Canceled and No-Show. Enforce legal transitions, persist timestamps and actor/context for each change, and allow controlled manual overrides with reasons. Provide timeout-based transitions for stale states and calculate SLAs (prep time, curbside wait). Expose the state and its history via API and streaming updates to keep the live tile accurate and consistent across brands.

Acceptance Criteria
Provider Status Mapping to Unified Lifecycle
Given an order with external status "created" or "pending", when ingested, then the unified state is New. Given an order with external status "accepted", "in_kitchen", or "preparing", when ingested, then the unified state is Preparing. Given an order with external status "en_route", "driver_assigned", or "on_the_way", when ingested, then the unified state is Arriving. Given an order with external status "arrived", "at_store", or "at_curb", when ingested, then the unified state is At Door. Given an order with external status "delivered", "picked_up", or "handed_over", when ingested, then the unified state is Handed Off. Given an order with external status "complete" or "closed" after Handed Off, when ingested, then the unified state is Completed. Given an order with external status "canceled" or "voided", when ingested, then the unified state is Canceled and terminal. Given an unrecognized external status, when ingested, then no unified state change occurs, an audit record with reason "unmapped_status" is created, and a warning is emitted to the event stream.
Valid State Transitions and Rejections
Given the current state S, when a transition to S' is requested, then it succeeds only if S→S' ∈ {New→Preparing, Preparing→Arriving, Arriving→At Door, At Door→Handed Off, Handed Off→Completed} or S' = Canceled or (S = Arriving and S' = No-Show). Given any attempt to skip forward or backward (e.g., New→Arriving, Completed→Handed Off), then the API responds 409 Conflict with error_code "illegal_transition", no state change is persisted, and the attempt is audit-logged. Given a duplicate transition request with the same request_id to the same target state, then the operation is idempotent, returns 200 OK, and no new audit record is appended. Given concurrent transition requests with different request_ids, then optimistic concurrency using version preconditions allows only one to succeed; losers receive 409 "version_conflict" and no state change is persisted.
Auditable State Change Log
Given any successful transition, then an immutable history record is appended containing: previous_state, new_state, transition_cause (provider|system|user), actor_id (nullable for provider/system), reason_code (required for user/manual), request_id, source (api|webhook|timeout), occurred_at (UTC ISO-8601), and sequence_number (monotonic per order). Given a request to GET /orders/{id}/history, then the API returns the full ordered history with sequence_number strictly increasing and no gaps. Given any attempt to modify or delete a history record, then the API responds 405 Method Not Allowed and no mutation occurs.
Manual Overrides with Role Controls
Given a user with role in {Manager, Lead}, when they request a manual transition with force=true, then the system allows transitions to any downstream state or to Canceled/No-Show, requiring reason_code from the configured list and optional free_text up to 280 characters. Given a user without the required role, when they attempt a manual override, then the API responds 403 Forbidden and no change occurs. Given a manual override transition, then the history entry sets transition_cause=user, includes reason_code, flags manual_override=true, and an event is emitted to the stream within 1 second.
Timeout-Based Transitions for Stale States
Given an order in Arriving for T_no_show minutes (configurable per location; default 10) with no subsequent At Door or Handed Off, then the system auto-transitions to No-Show, recording source=timeout and emitting an event. Given an order in At Door for T_handoff minutes (configurable; default 15) with no Handed Off, then the system emits an "at_door_stale" warning event and does not auto-complete the order. Given a system restart, then scheduled timeout jobs are resumed from durable storage and do not duplicate transitions (idempotent by request_id). Given a manual override that changes state before a scheduled timeout fires, then the pending timeout is canceled and no timeout transition occurs.
SLA Calculations and Exposure
Given a complete history, then prep_time_ms = max(0, ts_entered_Preparing - ts_entered_New) and curbside_wait_ms = max(0, ts_entered_HandedOff - ts_entered_Arriving) are computed and stored per order. Given any backfilled or overridden transition that affects these states, then SLA metrics are recomputed within 1 second and a recalculation event is appended to history. Given a request to GET /orders/{id}/state, then the response includes current_state, entered_at timestamps for each reached state, and SLA fields prep_time_ms and curbside_wait_ms. Given an order that never reached Arriving, then curbside_wait_ms is null.
API and Streaming Consistency
Given a transition occurs, then GET /orders/{id}/state reflects the new state within 1 second (p95) and a corresponding event with event_id and sequence_number is published to the stream. Given a subscriber with a saved cursor, when reconnecting, then events replay from that cursor without missing any; duplicates (if any) are detectable via event_id and can be ignored by clients. Given a state update request with If-Match ETag/version, then the update succeeds only when the precondition matches the latest version; otherwise 412 Precondition Failed is returned. Given an API or stream request authenticated with a token scoped to location/brand, then only matching orders are accessible; unauthorized access returns 401/403 and no data leakage occurs.
Real-time Tile Stream API
"As a shift supervisor, I want a live, reliable feed of orders to power our dashboard so that the team can act immediately as statuses change."
Description

Deliver a low-latency streaming interface (WebSocket/SSE) that publishes consolidated order tiles and incremental updates in under 1 second from ingestion. Include essential fields (customer identifier, vehicle notes, bay assignment, ETA, status, provider icon, aging) and support filters by location, brand, and status. Provide backfill on reconnect, sequence numbers for exactly-once client application, and pagination for historical tiles. Secure with token-based auth and enforce per-tenant rate limits. Designed to power the CurbPing dashboard and third-party displays without polling.

Acceptance Criteria
Sub‑second delivery of consolidated tile updates
Given a valid client subscription is established and a new order event is ingested at time t_ingest (server clock), When the event is consolidated into a tile update, Then the first corresponding message is delivered to the client with server publish timestamp t_pub such that t_pub − t_ingest <= 1000 ms for 1000 consecutive test events. Given incremental updates to an existing tile, When fields change (e.g., status, bayAssignment, ETA), Then each update is delivered within 1000 ms of ingestion for the same test set and in the same order as ingested for that tile.
WebSocket and SSE parity and stability
Given a valid token, When the client connects via WebSocket to the stream endpoint, Then the server completes the handshake and begins streaming events within 1 second of connection. Given the same token and filters, When the client connects via SSE, Then the event content, ordering, and sequence numbers are identical to those delivered over WebSocket for the same time window. Given either protocol is used, When an event is published, Then the message format and fields are identical across both protocols.
Tile payload completeness, schema, de‑duplication, and state transitions
Given a published or updated tile, When an event is received, Then the payload includes fields with valid types: tileId (string UUID), customerIdentifier (string), vehicleNotes (string|null), bayAssignment (string|null), eta (ISO 8601 string|null), status (Arriving|AtDoor|HandedOff), providerIcon (URL string), agingSeconds (non‑negative integer), brandId (string), locationId (string), sequenceNumber (monotonic integer), lastUpdated (ISO 8601 string). Given multiple source events refer to the same underlying order, When they are ingested, Then they are de‑duplicated into a single tile and only one consolidated update per state change is emitted. Given status changes occur, When updates are streamed, Then status transitions are monotonic in the order Arriving -> AtDoor -> HandedOff and never regress for the same tile.
Server‑side filtering by location, brand, and status
Given a client subscribes with filters locationId in [L1,L2], brandId = B, and status in [Arriving, AtDoor], When events are streamed, Then only tiles matching all provided filters are delivered in real time. Given the same filters, When the client first connects, Then the initial backfill includes only currently active tiles matching those filters as of connection time. Given a tile leaves the filtered set (e.g., status changes to HandedOff), When that change occurs, Then subsequent updates for that tile are no longer delivered to that subscriber.
Reconnect backfill and exactly‑once delivery via sequence numbers
Given the client stores the last received sequenceNumber, When it reconnects providing that sequence (via Last‑Event‑ID header or query parameter), Then the server delivers all missed events with sequenceNumber greater than the provided value and in strictly increasing order, with no duplicates. Given a transient disconnect within the backfill retention window, When the client reconnects, Then there are no gaps in observed sequence numbers for the subscription. Given the provided sequenceNumber is older than the backfill retention window, When the client reconnects, Then the server responds with an error indicating resynchronization is required and the client can recover by fetching paginated history before resuming streaming.
Historical tiles pagination for third‑party displays
Given a request for historical tiles with pageSize P and optional cursor C, When the request is made, Then the server returns up to P tiles ordered by lastUpdated descending, along with a nextCursor if more data exists. Given successive requests using the returned cursors, When pages are retrieved, Then there are no duplicated or missing tiles across pages and traversal is snapshot‑consistent despite concurrent new events. Given the final page is reached, When no more data remains, Then the server returns an empty or partial page with no nextCursor to indicate completion.
Token‑based security and per‑tenant rate limiting
Given a connection or history request without a valid bearer token, When the client attempts access, Then the server responds 401 (Unauthorized) and does not expose any stream data. Given a valid token scoped to tenant T, When subscribing or fetching history, Then only data belonging to tenant T is delivered and no cross‑tenant tiles are ever present. Given per‑tenant limits configured as L_conn connections/minute, L_msg messages/second, and L_hist history requests/minute, When a tenant exceeds any configured limit, Then the server enforces the limit by returning 429 (Too Many Requests) with a Retry‑After indicator and throttling further activity without affecting other tenants.
Guaranteed Delivery and Idempotency
"As a restaurant owner, I want assurance that no orders are lost or double-counted so that our team can trust the system during rushes."
Description

Harden the ingestion pipeline with durable queues, retry with exponential backoff, circuit breaking, and dead-letter handling. Use idempotency keys and dedup caches to prevent double-processing across retries and provider replays. Provide replay tooling from dead-letter queues and archived raw payloads, with audit trails for every action. Define SLOs (e.g., 99.9% within 3s end-to-end) and measure them, ensuring no order is silently dropped even during spikes or partial outages.

Acceptance Criteria
Backoff, Circuit Breaker, and Durable Queues Under Transient Failures
Given transient provider or downstream errors (timeouts, 5xx, network), When an event fails to process, Then it is enqueued durably before retry. Given a failed attempt, When retries occur, Then use exponential backoff starting at 1s doubling to a max of 60s with jitter and a maximum of 8 attempts. Given failure rate > 50% over 60s per integration, When evaluating the circuit, Then open the circuit for 120s and queue new events without attempting processing. Given the circuit is open, When half-open probes succeed (3 consecutive successes), Then close the circuit and drain the queue. Given a process crash or restart during retries, When the service resumes, Then no event is lost or processed more than once.
Idempotent De-duplication Across Retries and Provider Replays
Given multiple deliveries of the same event within 7 days (identical or semantically equivalent), When processed, Then only one state change is persisted and all subsequent deliveries are acknowledged without side effects. Given an event, When computing idempotency key, Then the key is deterministically derived from {provider, order_id, event_type, provider_event_id or normalized payload hash} and logged with the event. Given 10 concurrent duplicate deliveries, When processed, Then exactly one update occurs and the rest are counted as de-duplicates. Given a true follow-up event with a new provider_event_id, When processed, Then it is not de-duplicated.
Dead-Letter Routing, Alerting, and Safe Replay
Given an event exceeds max retries or is validation-malformed, When handling, Then it is moved to a dead-letter queue within 1s with a reason code and correlation/idempotency keys. Given a DLQ insertion, When monitoring, Then an alert is sent within 60s if DLQ depth increases beyond 50 or burn rate > 5/min. Given an operator selects DLQ events by time window or id and initiates replay, When replay executes, Then events are re-enqueued with original idempotency keys and processing results are recorded without double-processing. Given any replay action, When completed, Then an immutable audit entry records actor, timestamp, reason, item ids, and before/after states.
End-to-End Delivery SLO Measurement and Alerts
Given normal operations at 2x typical peak load, When ingesting events, Then 99.9% complete end-to-end (ingest receipt to persisted state + ack) within 3s and 50th percentile within 500ms. Given SLO tracking, When a 4-hour window burns > 2x the error budget, Then an alert is fired within 5 minutes. Given SLO reports, When the day ends, Then a daily report with p50/p90/p99/p99.9 latencies, success rate, and error budget consumption is generated and retained for 90 days.
No Silent Drops and Reconciliation
Given any inbound event, When processed, Then it is assigned a terminal outcome in {processed, de-duplicated, dead-lettered, quarantined} and visible in telemetry within 60s. Given the daily reconciliation job, When comparing provider counts vs ingested counts by integration, Then discrepancies are < 0.01% or all exceptions are present in quarantine with reasons and linked tickets. Given a telemetry outage, When restored, Then backlog metrics are backfilled so that every event within the last 24h has an outcome record.
Monotonic Order State Transitions Under Disorder and Concurrency
Given allowed transitions {Arriving -> At Door -> Handed Off}, When receiving out-of-order events, Then the resulting persisted state is the highest valid state and no regression occurs. Given duplicate "At Door" events, When processed, Then updated_at does not change and no duplicate notifications are sent. Given 10 concurrent events of mixed duplicates and out-of-order updates, When processed, Then exactly one monotonic state advancement occurs with a single notification. Given an invalid transition (e.g., Handed Off -> Arriving), When processed, Then it is ignored with an audit entry.
Raw Payload Archival, Integrity, and Controlled Access
Given an inbound event, When received, Then its raw payload and metadata are archived to WORM storage within 1s and retained for 90 days with a SHA-256 checksum. Given an authorized user with Replay role, When retrieving or exporting a payload, Then PII fields are redacted by policy and all accesses are logged. Given a replay request, When executed, Then the replay references the archived payload and verifies checksum integrity before enqueueing.
Provider Mapping and Setup Console
"As a system administrator, I want to self-serve setup and validate integrations so that we can onboard new brands and locations quickly without engineering help."
Description

Offer an admin UI to configure provider integrations: API keys, webhook secrets/targets, inbox addresses/filters for email parsing, and field/state mappings to the canonical model. Include test-send and sample-payload validation, template detection for new email formats, and safe rollout with per-location toggles. Support multi-brand, multi-location tenants with role-based access control and activity logs. Provide health indicators for each connector and guided troubleshooting when connectivity or parsing fails.

Acceptance Criteria
Configure Provider Credentials and Webhook Target
Given I am a Tenant Admin on the Provider Mapping and Setup Console When I select a provider and input required credentials (API key, webhook secret) and set a webhook URL Then the system validates required fields and disables Save until all validations pass And secrets are masked in the UI and redacted in logs at all times And clicking "Test Connection" sends a signed test event to the webhook and expects a 2xx within 5 seconds And on success the connector status updates to Connected with a timestamp; on failure it shows Error with a human‑readable reason and a Retry option And saving persists the configuration and writes an activity log entry with actor, scope, and before/after diffs
Set Up Email Inbox and Parsing Filters
Given I am configuring email ingestion for a provider When I set an inbound email address and define filters (From, Subject contains, SPF/DKIM must pass) Then the console validates the address format and filter syntax and prevents Save on invalid input And using "Send Test" with a sample email, the system displays pass/fail for each filter and shows why any filter failed And only emails matching all filters are marked Eligible; non‑matching emails are Ignored and logged with reason And inbox health displays Last Received timestamp and Bounce status
Auto-Detect and Validate New Email Template
Given I paste a raw email sample or upload an .eml When I click Auto‑Detect Then the system identifies provider/brand and a parser template with confidence >= 0.90 or requests manual confirmation/mapping And the extracted preview includes order_id, items, customer_name, pickup_eta, totals, and state hints when present And clicking Validate runs the parser against at least 3 stored samples with 0 critical parse errors and <= 5% non‑critical warnings And saving the template associates it to the selected provider and locations
Map Provider Fields and States to Canonical Model
Given I open Field & State Mapping for a provider When I map provider fields to the canonical model (order_id, external_id, vehicle, bay, customer_contact, timestamps) Then required canonical fields must be mapped before Save; unmet requirements show inline errors And I map provider statuses to canonical states (Arriving, At Door, Handed Off) with at least one mapping each And the preview renders a live tile for supplied sample payloads reflecting the mapped fields and state transitions And deduplication rules (provider + external_id) are configured and pass provided test cases with 100% dedupe accuracy
Per-Location Enablement with Safe Rollout
Given my tenant has multiple brands and locations When I toggle a provider integration On for selected locations Then only those locations begin ingesting events within 60 seconds; others do not ingest and log drops with reason "Location disabled" And disabling a location stops ingestion within 60 seconds without deleting configuration And all enable/disable actions create auditable entries with actor, scope (brand/location), and timestamp
RBAC and Activity Logging
Given roles exist (Tenant Admin, Brand Admin, Location Manager, Viewer) When users access the Setup Console Then permissions enforce: Tenant/Brand Admins can edit within their scope; Location Managers can toggle enablement for their own locations only; Viewers are read‑only And unauthorized actions are blocked with 403 and are not persisted And every create/update/delete writes an activity log entry capturing actor, role, IP, timestamp, scope, and before/after changes And activity logs are viewable and filterable by provider, brand, location, and actor
Connector Health Indicators and Guided Troubleshooting
Given connectors may experience connectivity or parsing issues When I view a connector’s Health panel Then I see status (Connected, Warning, Error), last success time, last failure reason, and error rate over the last 60 minutes And conditions trigger states: Warning when parse error rate > 5% or no events for 15 minutes; Error on 4xx/5xx auth/endpoint failures or bounces detected And clicking Guided Troubleshooting provides stepwise checks (credentials valid, webhook reachable, inbox active, template selected) with inline Re‑test buttons And after resolving issues and a successful re‑test, status returns to Connected within 60 seconds and the alert banner clears
Observability and Anomaly Alerts
"As a support lead, I want clear visibility and proactive alerts on data flow health so that we can resolve issues before they impact guests."
Description

Instrument end-to-end metrics (ingestion latency, error rate, dedup rate, event throughput per provider/location), structured logs with correlation IDs, and distributed traces across adapters, dedup engine, and state machine. Provide dashboards and alerting hooks (Slack/email/SMS) for SLA breaches, provider downtime, parsing drift, and abnormal patterns like sudden duplicate spikes. Include a searchable audit log of events and state transitions for support investigations and compliance.

Acceptance Criteria
Per-Provider/Location Metrics Dashboard
Given live order traffic across multiple providers and locations When a user opens the Observability dashboard and selects a time range (Last 15m, 1h, 24h, Custom) Then the dashboard displays ingestion latency (p50/p95/p99), error rate, dedup rate, and event throughput per provider and per location, plus global totals, with 1-minute resolution and UTC timestamps And metrics refresh at least every 15 seconds with a freshness indicator showing data lag (must be ≤ 30 seconds) And values match a synthetic load baseline within ±1% over a 10-minute validation run And metrics are filterable by provider, location, and event type; filters persist via URL parameters
SLA Breach Multi-Channel Alerting
Given configurable thresholds per metric at provider/location scope When a metric breaches its threshold for a sustained window (e.g., p95 ingestion latency > 3s for 5 consecutive minutes OR error rate ≥ 2% for 3 minutes) Then an alert is delivered to configured channels (Slack, email, SMS) within 2 minutes of breach confirmation including provider, location, metric, current value, threshold, window, incident ID, and links to dashboard/runbook And alert deduplication suppresses repeats for 15 minutes while posting status updates in the original thread/message And a recovery notification is sent within 2 minutes after the metric remains below threshold for 5 minutes And alert delivery is logged with correlation_id; failed deliveries retry with exponential backoff for 15 minutes
Provider Downtime Early Warning
Given an active location within configured business hours When event throughput for a provider at that location is zero for 10 minutes AND the 24h time-aligned baseline ≥ 1 event/min Then raise a Provider Downtime alert including provider, affected locations, last successful event timestamp, adapter/webhook health, and correlation details And if adapter 5xx rate ≥ 5% over 5 minutes OR provider webhook deliveries fail, include root symptom and HTTP error summaries And incident correlation links related SLA-breach alerts under a single incident ID to avoid noise
Parsing Drift Detection and Alerting
Given inbound payloads via email/webhook/API parsers When parse failure rate for any provider-location exceeds 2% over 5 minutes OR schema validator detects unknown/missing required fields in ≥ 50 events over 10 minutes Then raise a Parsing Drift alert containing provider, location, adapter version, redacted sample payloads (3), detected diffs, and link to parser validation logs And a canary synthetic order per provider runs hourly; two consecutive canary parse failures trigger an alert And a temporary parser override/patch can be toggled per provider with TTL; the action is recorded in the audit log with actor and reason
Duplicate Spike Anomaly Detection and Protection
Given deduplication baselines computed per provider-location over the past 7 days When duplicate rate exceeds max(10%, 3× baseline) for 5 minutes OR absolute duplicates ≥ 50 in 5 minutes Then raise a Duplicate Spike alert including suspected source, affected counts, top adapters, and sample correlation_ids And dedup engine collapses duplicates to a single live tile with idempotent state transitions; no more than one final tile per external order_id across adapters And the dashboard displays pre- and post-dedup throughput to verify dedup effectiveness
End-to-End Structured Logs and Distributed Traces
Given an order event traverses adapter → dedup engine → state machine When services emit logs Then each log line is structured JSON containing: timestamp (ISO-8601 UTC), level, service, environment, location_id, provider, order_id, event_type, state, correlation_id, trace_id, span_id; PII is redacted per policy And 100% of events carry a correlation_id from ingress to egress; ≥ 99% have a complete trace with spans for adapter, dedup, and state transitions, linked via W3C Trace Context And tracing sampling is configurable per environment (default: 100% non-prod, 20% prod) with on-demand elevation to 100% for a provider/location And logs/traces are searchable by correlation_id or order_id with p95 query latency ≤ 2 seconds for the last 24 hours
Searchable Audit Log of Events and State Transitions
Given a support agent investigates an order When querying the audit log by order_id, provider, location, correlation_id, or time range Then results include all events and state transitions with actor (system/user), action, previous_state → new_state, reason, timestamp (ISO-8601 UTC), correlation_id, and source adapter And the audit log is append-only and tamper-evident (hash-chained), retains records ≥ 90 days, and disallows delete/modify operations And p95 query latency ≤ 2 seconds for a 7-day range; CSV export is available up to 10,000 rows And access is restricted to authorized roles; all reads are themselves logged with actor and purpose

ShelfMap

Interactive shelf/bin map that assigns each order a clear location label and shows it right on the tile. Runners stage consistently; drivers find items without hunting. Cuts pickup dwell time, prevents wrong-bag handoffs, and frees dispatchers to manage flow instead of answering “Where’s my order?”

Requirements

Configurable Shelf/Bin Layout Builder
"As a shift lead, I want to configure our staging shelves and bin names so that runners and dispatchers follow the same map and place orders consistently."
Description

Provide a per-location configuration tool to model the physical staging area as an interactive map of shelves, racks, and bins. Admins define zones, rows/columns, custom bin names, capacities, temperature zones (hot/cold/ambient), and proximity to pickup doors. Configurations are versioned, clonable across stores, and validated to prevent duplicate labels. The layout is stored in CurbPing’s location settings and drives labeling, UI rendering, and assignment logic across web and mobile staff consoles.

Acceptance Criteria
Create New Layout With Zones and Bins
Given I am an Admin on Location Settings for a store with ShelfMap enabled When I open the Layout Builder and create zones (e.g., Hot Line, Cold Fridge, Ambient Rack), define rows/columns, add custom bin names, set per-bin capacities, assign temperature zones, and select the nearest pickup door for each bin And I click Save as Draft Then the layout is saved as Draft version 1 with a success message and summary counts of total zones and bins And the preview map renders rows/columns and bin labels exactly as configured And the saved layout object in location settings includes zones, bins, capacity, temperatureZone, and doorProximity fields conforming to the schema
Duplicate Bin Label Validation
Given I am editing a Draft layout with existing bin labels When I create or rename a bin to a label that already exists in the layout (case-insensitive, trimmed) Then the field shows an inline error "Bin label must be unique" And Save actions are disabled until all duplicate labels are resolved And duplicate detection checks across all zones, rows, and columns within the current layout version
Versioning and Publish Flow
Given at least one Draft layout exists When I click Publish and confirm Then the layout becomes the Active version and its version number increments by 1 And only the Active version is used for labeling, UI rendering, and assignment logic across staff consoles within 60 seconds And a version history entry is created with author, timestamp, and change summary When I select a previous version in history and click Roll Back Then that version becomes Active and the previously Active version is moved to history
Clone Layout Across Locations
Given I have Admin access to a source location with an Active layout and at least one target location When I choose Clone Layout, select one or more target locations, and confirm Then a new Draft layout is created in each target with identical zones, bin labels, capacities, temperature zones, and door assignments where matching door names exist And any bins mapped to doors that do not exist in a target are flagged as "Unassigned" with warnings listed before publish And duplicate label validation runs in each target before allowing save or publish And the cloning operation completes within 5 seconds per target location for layouts up to 500 bins
Capacity and Temperature Field Validation
Given I am configuring bins in the Layout Builder When I set a bin capacity to a non-integer or a value outside 1–999 Then I see an inline error "Capacity must be an integer between 1 and 999" and the value is rejected When I set a temperature zone Then only the allowed values {Hot, Cold, Ambient} are selectable and the default is Ambient And invalid temperature values cannot be persisted via UI or API
Door Proximity Mapping and Preview Grouping
Given the location has two or more pickup doors configured When I assign each bin a nearest door and optionally a distance in meters (0–50) Then distance accepts only numeric values 0–50 and defaults to 0 when left blank And the preview can be filtered by door, showing only bins mapped to the selected door And saved bin records reference door IDs (not just names) to ensure referential integrity
Intelligent Location Assignment & Suggestions
"As a dispatcher, I want the system to suggest an open bin for each order so that staging is fast and consistent without mental math."
Description

Automatically suggest and assign shelf/bin locations for each ready order using rules for capacity, temperature separation, proximity to pickup bays, special handling (allergies/large orders), and current occupancy. Provide override controls, closest-available fallbacks when preferred bins are full, and rebalancing prompts when dwell thresholds are exceeded. Update the order tile with the assigned location label and expose APIs/events so the rest of CurbPing (arrivals, dispatch view) can show the same location consistently.

Acceptance Criteria
Auto-Assign by Temperature, Capacity, and Proximity
Given an order is marked Ready with temperature_zone, size, allergy_flags, and (if available) assigned pickup bay And shelf/bin definitions include temperature_zone, capacity_units, proximity_to_bay, and current occupancy When the assignment engine runs Then the order is assigned in <=500ms to a bin matching the order’s temperature_zone, with sufficient remaining capacity for the order size, and without allergy conflicts And the selected bin is the nearest by proximity to the order’s pickup bay if known, otherwise nearest overall pickup zone And the bin’s occupancy is incremented and never exceeds its capacity And the assigned location label is written to the order record
Fallback to Closest Available Bin When Preferred Full
Given all preferred bins matching temperature and proximity are at capacity When assignment is attempted Then the system selects the next closest bin within the same temperature zone that satisfies capacity and constraints And the assignment reason is recorded as "fallback: preferred full" And no assignment is made that would exceed any bin’s capacity
Manual Override With Validation and Audit
Given a permitted staff user initiates Override for an order’s location When they select a target bin Then the system validates capacity and allergy constraints and surfaces a warning if violated And on confirm, the order is reassigned within 1s, the old bin occupancy decrements, the new bin occupancy increments, and the order record updates the location label And an event "order.location.changed" is emitted with user_id, from_location, to_location, reason="manual_override", and timestamp And the audit log records the action; if the user cancels, no changes are persisted
Dwell Threshold Rebalancing Prompt
Given an order has remained in its current bin beyond that bin’s dwell_threshold_minutes And there exists an alternative bin in the same temperature zone with lower congestion or closer proximity to the order’s pickup bay When the dwell check runs (every 60s) Then the system prompts staff with a suggested new bin including rationale and estimated improvement And on accept, reassignment completes within 1s with location label updates across UI and events emitted And on dismiss, the prompt snoozes for 10 minutes for that order
Cross-View Location Label Consistency
Given an order is assigned or reassigned a location When viewing Kitchen tiles, Dispatch view, and Arrival panel Then the exact same location label is displayed across all views and used in outbound SMS within 2s of the change And no view displays a stale or conflicting label during or after the update
API Events and Webhook Delivery for Location Changes
Given an order location is assigned, reassigned, or cleared When the change is saved Then a payload {order_id, location_id, location_label, action, reason, occurred_at} is published to the internal event bus and to registered webhooks And GET /orders/{id} reflects the current location fields within 1s And webhook delivery retries up to 3 times with exponential backoff on non-2xx responses and records delivery status
Occupancy Decrement on Pickup Handoff
Given a runner marks an order as Handed Off or the system receives a pickup completion event When the order state updates Then the assigned bin’s occupancy decrements immediately (<=500ms) and freed capacity is available for new assignments And if the order was reassigned during handoff, only the final bin decrements and no bin’s occupancy becomes negative
Interactive Staging Map (Drag-and-Drop)
"As a runner, I want to see an interactive map and move orders by dragging them so that I can stage and find bags quickly."
Description

Render a real-time, zoomable map of shelves/bins with occupancy counts and color states. Show each order’s location label directly on its tile; allow drag-and-drop to move orders between bins with snap-to-bin validation and instant updates to the order record. Include quick filters (order number, customer name, status), keyboard shortcuts, and accessibility support. Tapping an order highlights its shelf position for rapid retrieval. Integrate with CurbPing order lifecycle so movements and pickups reflect immediately across views.

Acceptance Criteria
Real-time Zoomable Staging Map with Occupancy and Color States
Given the ShelfMap view is open and the backend emits a bin occupancy change When a bin's count changes Then the bin's numeric occupancy and color state update in the UI within 1 second Given the user activates zoom controls or performs a trackpad/pinch gesture When zooming the map Then the map scales smoothly between 50% and 200% and tiles remain readable and clickable/tappable Given a color legend is displayed When bins render with color states Then the legend is visible and the color shown for each bin matches the legend's definition Given the real-time connection drops When the socket disconnects Then the UI shows a reconnecting indicator and falls back to polling every 5 seconds until the socket is restored
Drag-and-Drop Movement with Snap-to-Bin Validation
Given an order tile is draggable and a valid target bin is under the cursor When the user releases the tile over the bin Then the tile snaps into that bin within 300 ms and the order's location is updated in the backend successfully Given an order tile is released outside any valid bin When the drop occurs Then the drop is rejected, the tile returns to its original bin, and no backend update is made Given a target bin is at capacity When the user attempts to drop an order into that bin Then the drop is blocked with a visible tooltip 'Bin full' and no backend update is made Given two users attempt to move the same order concurrently When conflicting moves occur Then the last confirmed move wins, and the other client receives an automatic UI refresh reflecting the final bin within 1 second
Order Identification and Rapid Retrieval
Given an order tile is visible on the map When the tile is rendered Then the tile displays its exact location label (e.g., shelf-row-bin) inline at all supported zoom levels Given a user taps/clicks an order from the list or search results When the order is selected Then the map auto-scrolls to the associated bin and highlights it with a pulsing outline within 300 ms, and the highlight persists for 5 seconds Given multiple orders match a search When results are shown Then each matching order's bin is highlighted and the total matches count is displayed
Quick Filters by Order Number, Customer Name, and Status
Given orders exist on the map When a user types an order number, customer name, or selects a status in the filter input Then only matching order tiles remain visible and all others are dimmed/hidden within 300 ms at the 95th percentile for up to 5,000 orders Given multiple filter terms are applied When the filter is executed Then the result set applies AND logic across fields and updates incrementally as the user types Given a filter yields no results When the query is applied Then an empty-state message is shown and the map displays no highlighted orders Given the user clears the filter input When the filter is reset Then the full, unfiltered map view is restored immediately
Keyboard Shortcuts for Navigation and Actions
Given the ShelfMap has focus When the user presses '/' or Cmd/Ctrl+K Then focus moves to the filter input without opening the browser's native find Given the map is focused When the user presses Cmd/Ctrl+'+' or Cmd/Ctrl+'-' Then the map zooms in or out by 10% per keypress and displays the current zoom level Given an order tile is focused When the user presses Enter Then the order becomes selected and its bin is highlighted; when the user presses Esc, the selection and highlight are cleared Given the user presses '?' When the shortcut help is requested Then a non-modal cheatsheet lists all available shortcuts and they do not interfere with typing inside text inputs
Accessibility and Assistive Technology Support
Given a keyboard-only user navigates the ShelfMap When using Tab and Arrow keys Then focus moves predictably across bins and tiles, with a visible focus indicator on the active element Given screen reader users interact with bins and orders When elements receive focus Then bins announce their label and occupancy (e.g., 'Bin A3, 2 of 5 occupied') and orders announce order number, customer name, status, and current bin Given an order is successfully moved When the move completes Then an ARIA live region announces 'Order <ID> moved to Bin <Label>' Given WCAG requirements When evaluating the UI Then the ShelfMap meets WCAG 2.1 AA, including text contrast ≥ 4.5:1, non-text contrast ≥ 3:1, operable without pointer, and no keyboard traps
Lifecycle and Cross-View Synchronization
Given an order is moved to a new bin on ShelfMap When the move is confirmed Then the order's location updates in the order record and is reflected in all other CurbPing views (runner, dispatcher, order detail) within 1 second Given an order is marked as picked up from any view When pickup is recorded Then the order disappears from ShelfMap and the originating bin's occupancy decrements within 1 second Given order movement or pickup events occur When auditing activity Then an audit log entry is created with order ID, from_bin, to_bin (if applicable), user ID, timestamp (ms), and source view Given a backend update fails during a move When the error is returned Then the UI surfaces the error message and rolls back the visual move within 200 ms of receiving the error
Location Labels & QR Code Printing
"As a packer, I want to print a clear label with the shelf/bin location so that anyone can place or find the order without guessing."
Description

Generate clear, human-readable labels that include order ID, customer name, assigned shelf/bin label, and optional notes. Support thermal printers (ZPL/ESC-POS) and standard desktop printers; provide printer setup and fallback to on-screen display for handwritten labels if printers are unavailable. Include a QR code encoding the order and location metadata to support scan-based verification and rapid lookup. Ensure printed labels match the active layout configuration and update if the order is moved before printing.

Acceptance Criteria
Thermal Printer Label Generation (ZPL/ESC-POS)
Given a thermal printer supporting ZPL or ESC/POS is configured and online When a user selects Print Label for an order with order ID, customer name, shelf/bin label, and optional notes Then a single print job is dispatched within 2 seconds and the printer prints one label And the label includes human-readable Order ID, Customer Name, Shelf/Bin Label, and Notes (if present) matching the active layout positions and sizes And a QR code is present and scannable by a standard smartphone at 30 cm distance And the QR payload encodes order_id, location_label, and customer_name as UTF-8 text or JSON
Desktop Printer Support and Formatting
Given a desktop printer is selected When the user prints from Chrome, Edge, or Firefox on Windows or macOS Then a print-ready PDF is generated using the selected paper size (Letter or A4) at 100% scale with margins <= 5 mm And no content is clipped and all human-readable fields are at least 10 pt And the printed output matches the on-screen preview within ±2 mm for field positions And the QR code prints without pixelation and remains scannable
Printer Setup and Test Print Workflow
Given no printers are configured When the user opens Printer Setup Then the user can add a thermal printer (via IP or USB) or select a desktop/system printer And can set default printer and label layout per station And a Test Print sends a sample label and reports Success or Failure within 5 seconds And saved settings persist across logout and page reload
Fallback to On-Screen Label for Handwriting
Given no printer is configured or the most recent print attempt fails When the user attempts to print a label Then the app displays an on-screen label with Order ID, Customer Name, Shelf/Bin Label, and Notes in high-contrast, large type And the user can mark Handwritten Issued which logs a timestamped event on the order And the system stops automatic retries for that order unless the user explicitly retries
Real-Time Location Updates Before Printing
Given an order’s shelf/bin assignment changes before printing When the user prints the label Then the printed label reflects the latest shelf/bin assignment Given a print job is queued but not yet sent When the order’s shelf/bin changes Then the queued job is updated or replaced so the printed label uses the latest shelf/bin Given a label has already printed and the order is subsequently moved When the system detects the discrepancy Then it flags the order as Location Changed and prompts for a reprint
Scan-Based Verification and Rapid Lookup
Given a staff member scans the label’s QR code using the staff app or device camera When the scan is submitted Then the order details load within 1 second on a typical broadband connection and the current shelf/bin is highlighted Given the order location has changed since the label was printed When scanned Then the UI indicates Printed vs Current location mismatch and offers Reprint Given an invalid, unknown, or tampered QR payload When scanned Then the system rejects the scan and logs the attempt with timestamp and device info
Handoff Verification via Scan/Code Match
"As a front-of-house team member, I want to scan a code to confirm I’m handing the right bag to the right driver so that I avoid wrong-bag handoffs."
Description

Enable staff to confirm correct bag handoff by scanning the label QR with a device camera and optionally matching it to the driver’s SMS pickup code. On scan, surface the order’s shelf location and highlight it on the map; warn on mismatches or already-picked-up orders. Record who performed the handoff with timestamp and device ID, and update the order status to complete. Provide a no-scan fallback (code entry) and operate with low connectivity using local cache and queued sync.

Acceptance Criteria
Scan Completes Correct Handoff and Updates Order
Given a staff member is on the Handoff screen with an order labeled Ready and its QR label present When they scan the order’s QR code Then the app displays the order’s shelf/bin label and highlights that location on the ShelfMap within 1 second And shows a clear Complete Handoff action When the staff confirms completion Then the order status updates to Complete And the handoff record saves user ID, device ID, and UTC timestamp And the order timeline reflects the completion within 2 seconds
Pickup Code Match Required After Scan
Given the location setting Require pickup code match is enabled And a staff member has scanned a valid order QR When the driver’s pickup code is entered Then completion is allowed only if the code exactly matches the order’s code And on mismatch, the app blocks completion, shows an error without revealing the correct code, and logs the failed attempt with user ID, device ID, and timestamp And successful match proceeds to completion and logging as a single atomic action
ShelfMap Highlight on Scan
Given a scannable order currently staged on a shelf/bin When its QR code is scanned Then the UI surfaces the order’s shelf/bin label on the order tile And pans/zooms the ShelfMap as needed and visually highlights the exact location And the highlight remains visible until handoff is completed or the user navigates away
Warn and Block on Wrong Bag Scan vs Selected Order
Given a staff member has a specific order selected for handoff When they scan a QR that belongs to a different order Then the app displays a mismatch warning and disables completion for the selected order And it does not change the selected order’s status And it logs the mismatch attempt with user ID, device ID, and timestamp
Prevent Completion for Already Picked-Up Orders
Given an order’s status is Complete When its QR code is scanned or its code is entered Then the app displays Already picked up with the completion timestamp and completing user And it blocks any further completion action And it does not highlight a staging location And it logs the attempt without altering the order state
No-Scan Fallback via Code Entry
Given the device camera is unavailable or the QR is unreadable When the staff selects Enter code and inputs a valid order code Then the app retrieves the matching order, surfaces the shelf/bin label, and highlights it on the ShelfMap And the staff can complete handoff with the same validations and logging as the scan flow And invalid or not-found codes produce a clear error and do not allow completion
Offline Operation with Local Cache and Queued Sync
Given the device is offline or has low connectivity When a scan or code entry finds an order present in the local cache Then the app allows handoff completion locally, marks the event as Queued, and records user ID, device ID, and timestamp And no network error blocks the user from proceeding When connectivity is restored Then queued completions sync within 10 seconds using idempotent identifiers And if the server reports a conflict (order already completed), the local record is marked Failed to sync – conflict and the order is reverted to its server state, with a user-visible notification
Real-time Sync & Offline Resilience
"As a dispatcher working alongside runners, I want everyone’s screens to stay in sync even with spotty Wi‑Fi so that no one hunts for bags that were just moved."
Description

Ensure all changes to layouts, assignments, and order movements propagate instantly across all logged-in devices using WebSockets and delta updates. Handle concurrent edits with last-write-wins plus user warnings on conflicts. Cache the current map and orders for offline operation, queueing actions to sync when connectivity returns. Provide presence indicators (who is viewing/moving what) to reduce collisions and keep the team aligned during peak rushes.

Acceptance Criteria
Instant Layout Sync Across Devices
Given two or more devices are logged into the same location and viewing the ShelfMap When a user on one device renames a shelf, adds/removes a bin, or reorders bins Then all other devices display the exact same layout via delta updates within 1 second And no full-page reload occurs on any device And the persisted layout matches across a page refresh on all devices And the delta payload for a single label rename is under 5 KB and for a reorder of up to 50 bins is under 20 KB
Order Movement Real-time Propagation
Given two or more devices are viewing the same ShelfMap When a user drags an order tile from one bin/shelf to another and drops it Then all other devices reflect the new order location within 1 second And the initiating device receives an acknowledgement within 1 second or an error if not applied And other devices briefly show “moved by {user}” on the order tile for 2 seconds And no duplicate order tiles are visible on any device at any time
Concurrent Edit Conflict Handling
Given two users attempt to edit the same entity (order assignment, bin label, or shelf properties) within a short interval When both changes are submitted Then the system applies last-write-wins based on authoritative server timestamps And the overwritten editor’s client displays a non-blocking warning within 1 second: “Your change was overwritten by {user} at {time}” And the final state is consistent on all devices within 1 second with no partial updates And a conflict event is logged with entity ID, user IDs, and timestamps
Offline Cache and Action Queue
Given a device loses internet connectivity while the ShelfMap is open When the user continues to view and interact with the ShelfMap Then the latest known map layout and order list remain available from cache within 1 second And any actions (move order, change bin label, assign/unassign) are queued locally with durable storage And the UI shows an “Offline” indicator and the count of queued actions And no queued action is lost across app reloads for at least 24 hours
Reconnection and State Reconciliation
Given there are queued offline actions When the device reconnects to the internet Then the client syncs by sending queued actions in original order and applying server deltas And conflicts are resolved with last-write-wins; any rejected action triggers a warning toast referencing the affected entity And successful actions are removed from the queue and the “Offline” indicator is cleared And up to 50 queued actions synchronize and reconcile within 2 seconds on reconnect
Presence Indicators for Viewing and Moving
Given multiple users are collaborating on the same ShelfMap When a user focuses a bin/order or starts dragging an order Then other devices display a presence indicator with the user’s initials on that bin/order within 1 second And while an order is being dragged, other users see “in use by {user}”; attempts to move it show a warning but remain allowed, following conflict policy And presence indicators expire within 3 seconds of inactivity or disconnect and never persist as ghosts And a maximum of 5 avatars are shown per bin/order with a “+N” overflow indicator

QuickStart Drills

Two‑minute, auto‑sequenced practice modules that simulate your top curbside moments—staged arrivals, bay conflicts, late orders, and add‑ons—using your actual bay layout and SMS templates. New hires warm up pre‑shift, managers trigger fast refreshers between rushes, and everyone builds muscle memory without risking live guests.

Requirements

Auto-Sequence Scenario Engine
"As a shift manager, I want staff to run short, realistic drills that mirror our busiest curbside moments so that they build muscle memory without tying up the line."
Description

Implements a configurable engine that assembles two-minute practice modules from a library of micro-scenarios (staged arrivals, bay conflicts, late orders, add-ons). The engine uses store-specific settings (hours, prep times, surge thresholds) to generate realistic flows, enforces timeboxing to two minutes, and supports difficulty tiers. It deterministically seeds scenarios for repeatability while allowing variation across runs. The engine exposes an API for the UI to advance steps, trigger simulated events (arrival pings, add-on requests), and collect decisions/timings. It integrates with CurbPing configuration but operates on synthetic orders to avoid impacting production. Outcome data (choices, timestamps) are emitted for scoring and analytics.

Acceptance Criteria
Two-Minute Timebox Enforcement
Given a training module is created with timebox = 120 seconds When the run starts Then the engine stops the module at or before t0 + 120 seconds wall-clock And no step or simulated event timestamp exceeds t0 + 120 seconds And a run summary is emitted immediately upon stop And the API surface exposes remaining_time that decrements to 0 and never negative
Deterministic Seeding and Variation
Given storeId, difficulty, and seed S with unchanged configuration version When two modules are generated with the identical inputs Then microScenarioIds[], stepOrder[], parameterHashes[], and eventSchedule[] are identical And module metadata includes seed = S and configVersion Given the same inputs except a different seed S2 ≠ S When a module is generated Then at least one of microScenarioIds[], parameterHashes[], or eventSchedule[] differs while all constraints (timebox, config rules) remain satisfied
Store Configuration-Driven Flow
Given a store config with bays = 6, prep_time_mean = 12 minutes, surge_threshold = 8, open_hours = 10:00–22:00, smsTemplateId = T1 When a module is generated at 15:00 local time Then no step assigns a bay index < 1 or > 6 And at least one simulated arrival references a valid bay from the store layout And any simulated late-order delay is between 0.5× and 1.5× prep_time_mean And when simulated active orders exceed 8, a surge/backlog micro-scenario is scheduled within 30 seconds And any templated message content resolves from template T1 (rendered in payload) without transmission
Micro-Scenario Assembly and Sequencing
Given a library containing staged_arrival, bay_conflict, late_order, and add_on micro-scenarios When creating a 120-second module at difficulty = Medium Then the module contains 3 to 5 micro-scenarios with at least two distinct types And the sum of planned durations for all micro-scenarios is ≤ 120 seconds And sequencing enforces pre/post conditions (no bay reassignment without release or conflict resolution) And each micro-scenario includes explicit entry and exit markers in the step payload
API Interface for Steps and Events
Given a running module M When the UI calls POST /modules with storeId, difficulty, and seed Then the API returns 201 with moduleId and the first step payload When the UI calls POST /modules/{id}/advance with a valid stepToken Then the next step payload is returned within 300 ms at p95 and the previous step becomes immutable When the UI calls POST /modules/{id}/events with type ∈ {arrival_ping, add_on_request} Then the event is recorded, timestamped server-side, and reflected in subsequent step payloads And all write operations keyed by stepToken are idempotent And invalid inputs return 400 with machine-readable error codes; unknown moduleId returns 404
Synthetic Mode Isolation from Production
Given trainingMode = true When a module runs end-to-end Then no records are created or mutated in production order, dispatch, or messaging tables And no SMS, push, or live notifications are sent to real recipients And all synthetic orders/messages are tagged with moduleId and persisted only in the training namespace And production-facing metrics (ETA, bay utilization) remain unchanged during the run And audit logs show zero cross-environment writes
Outcome Data Emission for Scoring
Given a completed module run When telemetry is requested via GET /modules/{id}/telemetry or via the analytics sink subscription Then the payload includes moduleId, storeId, difficulty, seed, configVersion, and start/end timestamps And contains one record per step with stepId, microScenarioType, userDecision, serverTimestamp, clientTimestamp, and responseLatencyMs And includes derived metrics totalCorrect, totalIncorrect, and averageResponseTimeMs And data becomes available within 5 seconds of module completion And the payload validates against JSON Schema quickstart.telemetry.v1
Live Bay Layout Integration
"As a new hire, I want drills to use the same bay layout I’ll see on shift so that my practice matches real-world decisions."
Description

Imports and references the restaurant’s actual bay configuration (count, labels, ordering, disabled bays) from CurbPing settings and renders a lightweight visual layout during drills. Drill steps must honor real assignment rules (e.g., ADA bay protection, blocked bays) and reflect dynamic constraints such as temporarily closed bays. The visualization works across mobile and tablet, supports left-to-right or right-to-left numbering, and gracefully degrades for locations without marked bays (list view). This ensures decisions trainees make in drills transfer directly to live operations.

Acceptance Criteria
Import Bay Configuration From Settings
Given a location has bay settings in CurbPing (count, labels, explicit order, disabled flags), When a QuickStart Drill is launched, Then the rendered layout shows the exact bay count in the configured order with matching labels, and any disabled bays are visually disabled and cannot be selected. Given bay settings are updated before drill start, When the user starts or refreshes the drill, Then the layout reflects the latest settings within 5 seconds of retrieval. Given the settings service is unavailable, When the drill attempts to load the layout, Then a retriable error with a “Retry” action is shown and no stale layout is rendered.
Responsive Visual Layout (Mobile and Tablet)
Given devices from 320px to 1024px width in portrait and landscape, When the bay layout renders, Then all bays fit within the viewport without horizontal scroll and maintain minimum 44px touch targets. Given a user scrolls or interacts with the layout, When rendering occurs, Then interaction latency is under 100ms and frame rate is maintained at or above 30fps on reference devices. Given WCAG AA requirements, When text and icons display in the layout, Then color contrast is ≥ 4.5:1 and controls are keyboard-accessible with visible focus states.
Honor ADA and Disabled Bay Rules in Drills
Given one or more bays are marked ADA-only in settings, When assigning a non-ADA order in a drill, Then ADA-only bays are not auto-suggested and are not selectable; a tooltip or note explains the restriction. Given an order is marked ADA, When suggesting a bay, Then an available ADA bay is suggested first; if none are available, the next eligible non-ADA bay is suggested and the reason is indicated. Given a bay is flagged disabled or blocked, When selecting or auto-assigning, Then that bay is excluded from suggestions and cannot be selected.
Dynamic Bay State Updates During Drills
Given a bay is open at drill start, When it is marked temporarily closed in CurbPing settings during an active drill, Then within 10 seconds the drill UI reflects the closed state and removes it from suggestions and selection. Given a bay transitions from closed to open during a drill, When the next step begins, Then the bay becomes eligible for suggestions and selection immediately. Given an order was assigned to a bay that becomes closed mid-drill, When advancing to the next step, Then the drill prompts for reassignment and enforces current eligibility rules.
Numbering Direction Support (LTR/RTL)
Given numbering direction is set to RTL in settings, When rendering bays 1..N, Then Bay 1 appears at the far right and numbering decrements leftward, and keyboard/focus order follows the visual order. Given numbering direction is switched to LTR, When the layout re-renders, Then Bay 1 appears at the far left and numbering increments rightward consistently across all steps. Given a drill uses the location’s numbering direction, When the drill starts, Then the direction is read from settings and applied without requiring user override.
Graceful Degradation for Locations Without Marked Bays
Given a location has unmarked parking (no bay map configured), When launching a drill, Then a list view is presented instead of a visual map, showing assignable positions consistent with live operations. Given assignment constraints (ADA, temporarily closed equivalents) apply, When using the list view, Then the same rules are enforced and clearly indicated. Given performance budgets, When the list view is used, Then no map assets are loaded and initial render completes in under 2 seconds on a 3G fast profile.
Parity With Live Assignment Outcomes
Given a suite of 20 canonical scenarios mirroring live arrivals (mix of ADA flags, disabled/closed bays, numbering directions), When running the same inputs through the drill’s assignment logic, Then suggested and enforced assignments match the live engine outputs in 20/20 cases. Given logging is enabled, When a mismatch occurs, Then the drill logs the input, chosen bay, live engine bay, and rule rationale for diagnostics.
SMS Template Mock Messaging
"As a trainer, I want trainees to practice using our real SMS wording so that their responses are fast and on-brand during rushes."
Description

Loads the store’s existing SMS templates (greeting, arrival confirmation, bay instructions, add-on offers) and simulates two-way messaging within the drill UI. Outgoing messages are rendered using production templates and merge fields; incoming customer replies are simulated by scenario scripts. No real messages are sent. The module supports branching based on trainee selections and timing, records message choices and edits, and validates against best-practice phrasing. This lets teams rehearse consistent, brand-aligned communication without touching live guests.

Acceptance Criteria
Template Load and Merge Render
Given a store has active SMS templates with merge fields and a drill is initialized with sample order, customer, and bay data When the trainee triggers the "Send Greeting" action in the drill Then the system renders the outgoing message using the production template with all merge fields resolved from drill data And no unresolved placeholders or merge syntax remain in the rendered text And the rendered text equals the result produced by the production renderer for the same inputs And if any required merge value is missing, the send action is blocked and an inline error identifies each missing field
Simulated Two‑Way Messaging (No Real Sends)
Given a drill scenario with scripted customer responses and an SMS provider configured for the account When the trainee sends any message within the drill Then the trainee sees the simulated customer reply generated by the scenario script within the configured response delay window And no outbound requests are made to external SMS gateways for the drill session And all messages in the transcript are visibly labeled as "Simulated" for the session
Branching by Selection and Timing
Given a scenario with defined branches for trainee choices and time thresholds When the trainee selects an option or exceeds a branch's time threshold Then the next message and prompts follow the branch definition matching the selection or timing condition And the transcript records which branch node was taken with a unique identifier And repeating the drill with the same inputs follows the same branch deterministically
Best‑Practice Phrasing Validation
Given best‑practice rules configured for each template type (e.g., include bay number for bay instructions; tone and length limits) When the trainee edits an outgoing message before sending Then the validator scores the message and surfaces pass/fail and specific rule violations prior to send And messages missing mandatory elements cannot be sent until corrected And the final score and rule outcomes are stored with the drill attempt
Action and Transcript Audit Trail
Given a trainee runs a drill session When the trainee previews, edits, or sends a message Then the system logs timestamp, template key/version, rendered text, edit diff, selected option, branch id, and validation result And the session report exposes the full transcript and action log to managers after completion And data is retained according to training data retention settings
Bay Context in Rendered Messages
Given the store's bay layout is loaded and the scenario assigns Bay 3 to the customer When the trainee sends the bay instruction message Then the rendered message includes the correct assigned bay number and any location descriptors from the store layout And if the scenario simulates a bay conflict, the message reflects the updated bay assignment per scenario rules
One-Tap Drill Launcher & Assignments
"As a manager, I want to send short practice drills to specific team members with a single tap so that we can tighten execution without disrupting service."
Description

Enables managers to launch predefined or auto-generated drills with one tap from the dashboard, assign them to individuals or roles, and deliver access via short links (SMS, email, QR). Supports ad-hoc starts between rushes and scheduled pre-shift warmups. Respects role-based access controls, tracks acceptance and completion, and enforces two-minute duration. Includes quick relaunch for repeat practice and lightweight device check to ensure drills run smoothly on shared back-of-house tablets and personal phones.

Acceptance Criteria
One-Tap Launch from Dashboard
Given a manager is on the dashboard and a predefined or auto-generated drill with default assignees is visible When the manager taps Launch on the drill card Then the drill session is created without additional configuration steps and a confirmation toast appears within 2 seconds And the session appears in the Active Drills list with status "Launched" and a UTC start timestamp And the drill uses the current bay layout and SMS templates configured for the location
Role-Gated Launcher Access
Given role-based access controls are enabled When a user with Manager (or higher) role views the dashboard Then the Launch and Relaunch controls are visible and enabled And when a user without sufficient role attempts to access launch or assignment via UI or direct URL Then the controls are hidden in UI and server responds with 403 on direct access, and an audit log entry is recorded with user, time, and action
Assign to Individuals and Roles with Short-Link Delivery
Given a manager selects one or more individual users and/or one or more roles for a drill When the manager taps Assign & Send Then unique, non-guessable short links are generated per assignee and tied to their identity And delivery is attempted via the selected channels (SMS, email) within 15 seconds, and a QR code is rendered immediately for on-device scanning And delivery outcomes (sent, failed, bounced) are recorded per channel and visible in the drill details And only the intended assignee can access the drill via their link; others receive Access Denied and the attempt is logged And links expire at the earlier of 10 minutes after drill end or 90 minutes after issuance
Ad-Hoc and Scheduled Starts
Given a manager chooses to start a drill Now or Schedule it for a future date/time in the location’s timezone When Now is selected Then links are delivered immediately and the session state is Launched When a future time is selected Then the drill is listed as Scheduled with the correct timezone, and links are automatically delivered 5 minutes before start and again at start time And scheduled starts fire without the manager being online, and are resilient to service restarts And if the scheduled time is less than 2 minutes from creation, links are delivered immediately and the session is marked Scheduled (imminent)
Acceptance, Completion, and Two-Minute Enforcement
Given an assignee opens their short link on a supported device When they tap Start Drill Then an acceptance timestamp is recorded and the assignee’s status becomes In Progress And a visible countdown starts from 2:00 and the session auto-ends at 0:00, recording completion and elapsed time And the assignee cannot submit or exit as Complete before 2:00; abandoning the page for >30 seconds auto-ends as Incomplete And the manager dashboard updates in real time with counts for Accepted, In Progress, Complete, and Incomplete And completion and acceptance are exportable with user, device, and timestamps
Quick Relaunch for Repeat Practice
Given there is at least one prior drill session When a manager taps Relaunch on the most recent session Then a new session is created reusing the prior drill configuration and assignees with no additional steps And new short links are generated and the prior session’s links are invalidated And the Relaunch action completes within 2 seconds and the new session appears as Launched
Device Preflight Check on Shared Tablets and Personal Phones
Given an assignee opens a drill link on a device When the preflight check runs Then it verifies browser (Chrome/Edge 110+ or Safari 15+), viewport width >= 320px, and network round-trip latency <= 800 ms And on pass, the drill loads; on fail, start is blocked with an actionable message and a QR handoff for alternate device And preflight result (pass/fail, device, browser, latency) is logged and visible to managers in the session details
Real-Time Scoring and Micro-Feedback
"As a team member, I want instant, clear feedback on what I did well and what to fix so that I improve with every two-minute practice."
Description

Scores each drill attempt on speed and correctness against scenario-specific rubrics (e.g., time-to-assign-bay, conflict resolution accuracy, adherence to SMS guidelines). Provides immediate, bite-sized feedback after each step and a succinct summary at the end, highlighting one improvement tip and one strength. Stores attempt history per user, supports pass thresholds, and unlocks refresher recommendations when performance dips. Feedback is phrased for quick comprehension on mobile and avoids lengthy reading during pre-shift windows.

Acceptance Criteria
Step-Level Micro-Feedback on Mobile
Given a user completes any drill step When the step result is evaluated Then a micro-feedback message appears within 1.0 second of the user action And the message is no longer than 180 characters and contains no more than 2 sentences And the message includes exactly one actionable suggestion when the step is incorrect, otherwise one positive reinforcement phrase And the message is dismissible and does not block the next action And the message’s readability (Flesch-Kincaid Grade Level) is ≤ 7
End-of-Drill Summary With One Strength and One Tip
Given the user completes the final step of a drill When the drill ends Then a summary appears within 2 seconds And it shows the total score as a whole-number percentage and a Pass/Fail status based on the scenario’s threshold And it includes exactly one top strength and exactly one improvement tip, each ≤ 120 characters And it provides a step-by-step breakdown accessible from the summary without reloading the page
Scenario-Specific Rubric Scoring Engine
Given a drill scenario has a rubric with defined metrics and weights When a user runs and completes the scenario Then the system computes metric values including: time_to_assign_bay (seconds from arrival signal to bay assignment), conflict_resolution_accuracy (0, partial, or 1), sms_guideline_adherence (0–100%), and late_order_handling correctness (0 or 1) And the total score = Sum(weight_i × normalized_metric_i) rounded to the nearest whole percent And the rubric version used is recorded with the attempt and remains constant for that attempt And changing the rubric for future runs does not alter previously stored scores
Pass Thresholds and Refresher Recommendation
Given a scenario has a pass threshold T (%) When an attempt completes Then mark Pass if score ≥ T else Fail And if the current attempt fails OR there are ≥ 2 failures in the user’s last 5 attempts for this scenario Then display a “Refresher recommended” banner and unlock the relevant refresher module And the refresher module targets the lowest-scoring metric from the latest attempt And if the user achieves two consecutive Pass attempts (score ≥ T), remove any active refresher recommendation for this scenario
Attempt History Storage and Retrieval
Given any drill attempt completes When the system saves the attempt Then it persists: user_id, scenario_id, attempt_id, started_at_utc, completed_at_utc, rubric_version, per_metric_scores, total_score_percent, pass_boolean, strength_text, tip_text And records are retained for at least 180 days And when a user requests their history, the last 30 attempts return in reverse chronological order within 1 second (p95) And the same history is available across sessions and devices for the same user
SMS Guideline Adherence Validation
Given a step requires composing an SMS response When the user submits the message Then evaluate adherence to the scenario’s configured template such that: length ≤ 160 characters; required tokens (e.g., {bay} and either {pickup_name} or {order_number}) are present per template; and no term from the scenario’s banned_terms list appears And assign adherence: 100% if all conditions met; 50% if exactly one condition fails; 0% if two or more conditions fail And the micro-feedback explicitly names the first missing or violating element
Readiness Analytics Dashboard
"As an operator, I want a quick view of team readiness and trends so that I can target coaching and reduce curbside delays."
Description

Aggregates drill data into a manager-facing dashboard showing completion rates, average scores, median response times, and common error patterns by scenario type and role. Provides filters by date range, shift, and location; offers CSV export. Surfaces coaching insights (e.g., "bay conflicts resolved late on weekends") and suggests targeted drills to assign. Integrates with the existing team roster to attribute results to users while honoring privacy settings and access controls.

Acceptance Criteria
Aggregated Metrics by Scenario and Role
Given a dataset of drills with known assigned_count, completed_count, scores, and response_time_sec across multiple scenario types and roles When a manager opens the dashboard with no filters applied Then the dashboard displays, for each scenario type and each role: - completion_rate_pct = completed_count / assigned_count rounded to 1 decimal - average_score rounded to 1 decimal - median_response_time_sec rounded to whole seconds And totals for each metric across all scenarios and roles are shown And a "Last updated" timestamp in the account timezone is shown And all displayed values match the expected calculations for the dataset exactly after rounding
Filter by Date Range, Shift, and Location
Given drills span multiple dates, shifts, and locations When the manager selects a custom date range, any combination of shifts, and any combination of locations and applies filters Then all metrics, error patterns, and insights recalculate using only drills within the intersection of the selected filters And the selected filters are visibly indicated And clearing filters returns the dashboard to the unfiltered state And all timestamps and shift boundaries are evaluated in the manager’s configured timezone
CSV Export of Filtered Dashboard Data
Given the dashboard is filtered to a specific date range, shifts, and locations When the manager clicks "Export CSV" Then a UTF-8, comma-delimited CSV with a header row is downloaded within 10 seconds And it contains one row per scenario_type x role combination present in the filtered results with columns: date_range, locations, shifts, scenario_type, role, assigned_count, completed_count, completion_rate_pct, average_score, median_response_time_sec, top_error_pattern_1, top_error_pattern_2, top_error_pattern_3 And all numeric values match the on-screen metrics after rounding And the export respects access controls (only locations the user can access) and privacy settings (no disallowed PII) And exports succeed for up to 10,000 rows without truncation
Coaching Insights and Targeted Drill Suggestions
Given the last 14 days include ≥15 drills for a scenario type on weekends and the weekend median response_time_sec is ≥20% higher than the weekday median for the same role When the manager views the Insights panel Then an insight describing the weekend lag (e.g., "resolved late on weekends") appears with the affected role, sample size, and weekend vs. weekday medians And each insight provides an "Assign drills" action that preselects at least one recommended QuickStart Drill mapped to the issue And no more than 5 insights are shown at once, ordered by largest performance gap first
Common Error Patterns Identification
Given drills record error tags per attempt (e.g., "wrong bay", "late handoff", "missed SMS") When the manager views error patterns for a filtered scope Then the dashboard shows the top 3 error patterns per scenario type and per role with counts and percentages of affected drills And ties are broken by most recent occurrence And patterns with fewer than 3 occurrences are not shown
Roster Integration and Privacy/Access Controls
Given the organization’s team roster defines users, roles, locations, and privacy settings When the dashboard attributes results Then each drill result is attributed to a roster user ID and role for aggregation And managers/owners can view only the locations they manage; staff-level users cannot access the dashboard And location scoping is enforced so users see only locations assigned to them And if a user has analytics privacy enabled, their name is hidden to non-admin roles and replaced with an anonymized label while still included in aggregates and exports
Performance and Data Freshness
Given continuous drill activity When a drill is completed Then its results appear in the dashboard within 60 seconds And the dashboard initial load for the last 30 days across up to 5 locations, 100 users, and 10,000 drills completes in ≤2.5 seconds over a 50 Mbps connection And applying or clearing filters updates visible metrics in ≤1.0 second
Sandbox Isolation and Data Guardrails
"As a compliance-conscious operator, I want training completely segregated from live operations so that practice never impacts real customers or metrics."
Description

Runs drills in a fully isolated training namespace with synthetic orders, preventing any creation, modification, or notification in production systems. Disables external messaging providers for drills, clearly labels training UI, and logs all drill interactions separately for audit. Enforces rate limits and data retention policies, and provides admin controls to purge training data. This guarantees practice cannot affect live guests, metrics, or order flow while allowing realistic simulation.

Acceptance Criteria
Drill Runs in Isolated Namespace
- Given a user starts a QuickStart Drill, when the system provisions the session, then a unique training namespace is created per session scoped to the org with training=true. - Given create/update/delete operations on Orders, Arrivals, Bays during a drill, when executed, then writes occur only in training datastores and production datastore write counters for these entities remain unchanged (0 delta) for the drill duration. - Given the drill session ends, when production stores are queried, then no new or modified production Orders, Arrivals, Bays, or Tickets exist and counts equal pre-drill baselines. - Given drill records, when inspected, then each record has is_training=true and a non-null training_session_id.
External Messaging Providers Disabled in Drills
- Given a drill step triggers an SMS or call, when executed, then no outbound requests are sent to external providers (e.g., Twilio) and network egress to provider domains is blocked or stubbed. - Given the same step, then a simulated message is captured in the training message store with to, body, template_id, timestamp, and is_training=true. - Given provider API logs, when compared before/after the drill, then total sent/received counts remain unchanged. - Given inbound webhooks from providers, when received for training sessions, then they are rejected with HTTP 403 and logged to the training audit log.
Training UI Clearly Labeled and Visually Distinct
- Given any training screen, when rendered, then a persistent header banner displays "Training — Sandbox (No live guests)" and a watermark "Training" is visible on order and arrival panels. - Given the URL, when in a drill, then it contains the path segment /training/. - Given theme tokens, when training mode is active, then the primary theme uses the training palette token and meets WCAG AA contrast. - Given screenshots captured above the fold, when compared, then at least two distinct training labels are visible in every key screen (Orders, Arrivals, Bay Map).
Training Events Logged Separately and Excluded from Production Metrics
- Given drill interactions (check-in, bay assign, add-on), when emitted, then they are written to training_event_log with org_id, user_id, training_session_id, type, ts and are not written to prod_event_log. - Given analytics dashboards (Wait Time, Bay Utilization), when drills run, then KPI values do not change relative to pre-drill baselines for the same time window (0 delta attributable to training events). - Given an export job run with filter is_training=false, then zero training records appear in the export. - Given an audit query by training_session_id, when executed, then a complete ordered trail of training events exists with no gaps.
Training Rate Limits Enforced
- Given a user, when starting drills rapidly, then no more than 3 drill sessions may be created per user within any rolling 2-minute window; excess attempts return HTTP 429 with code TRAINING_RATE_LIMIT_USER and a retry-after value. - Given an org, when drills are started across the org, then no more than 20 sessions per org within any rolling 5-minute window; excess attempts return HTTP 429 with code TRAINING_RATE_LIMIT_ORG. - Given simulated messages during drills, when emitted, then no more than 60 simulated messages per org per minute are recorded; excess are dropped and logged with code TRAINING_MSG_RATE_DROP and dropped_count. - Given the rate-limit window elapses, when a new request is made, then session creation succeeds (HTTP 201).
Training Data Retention and Admin Purge Controls
- Given retention policy set to 30 days, when a training record age exceeds 30 days, then it is deleted by a scheduled job and is no longer retrievable via APIs within 24 hours of eligibility. - Given an admin with Owner role, when invoking "Purge Training Data Now" in the admin UI with scope session or org, then the system deletes selected training records and attachments within 2 minutes and surfaces a confirmation with purge_id. - Given API POST /admin/training/purge with valid scope and auth, when called, then it responds 202 Accepted with purge_id and progress is available at GET /admin/training/purge/{purge_id} until status=Completed. - Given the audit log, when a purge completes, then an immutable entry exists with actor, scope, counts_deleted, started_at, finished_at, and checksum/hash.

RolePath Tracks

Role‑specific training flows for Curb Captain, Expo‑Fire, Ghost Kitchen Dispatcher, and more. Each track loads the exact screens, hotkeys, and decisions for that role, progressing from basics to advanced moves like BaySwap overrides and Instant Adjust. Cuts ramp time and reduces permission mistakes.

Requirements

RolePath Selection & Assignment
"As an operator, I want to assign the correct role track to each staff member so that they only see relevant screens and can ramp up quickly."
Description

Allow operators to create, assign, and manage role‑specific training tracks (e.g., Curb Captain, Expo‑Fire, Ghost Kitchen Dispatcher) per user and per location. Support shift‑based assignment, quick role switching for cross‑trained staff, and defaults by device/kiosk. When a user signs in via SMS link or browser session, automatically load the correct RolePath. Maintain an audit trail of assignments and changes, and provide safeguards for multi‑location operators. Integrate with existing user model and location concepts in CurbPing without requiring apps or additional hardware.

Acceptance Criteria
Create and Assign RolePath per User and Location
Given an operator with Admin permission at location L When the operator creates a RolePath named "Curb Captain" and assigns it to user U for location L via UI or API Then the RolePath is created with default permissions and hotkeys And the assignment is saved and visible in U's profile for location L within 2 seconds And no assignment is created for any other location And the system enforces that user U exists and is provisioned at location L And an audit entry is created capturing actor, target user, role, location, timestamp (UTC), and source (UI or API)
Shift-Based RolePath Activation and Reversion
Given user U has an upcoming shift S (start and end times) at location L with RolePath R assigned When the current time reaches the start of shift S Then U's active RolePath switches to R within 30 seconds And when the current time is past the end of shift S Then U's active RolePath reverts to their default within 60 seconds And any manual override during S takes effect within 2 seconds and persists until end of S or until removed
Quick Role Switching for Cross-Trained Staff
Given user U is cross-trained with RolePaths R1 and R2 at location L When U initiates a role switch via Quick Switch control or assigned hotkey Then the active RolePath changes within 2 seconds without page reload And screens, hotkeys, and permissions update to match the new RolePath immediately And any in-progress order/task views persist without data loss And an audit entry records the switch with timestamp, device identifier, and location
Device/Kiosk Default RolePath Application
Given device D at location L is configured with a default RolePath R When any user signs in on device D Then RolePath R is applied unless an active shift or user-specific assignment overrides it And the UI displays a banner indicating the RolePath source (Device Default, User Assignment, or Shift) And applying R on device D does not change the user's default RolePath on other devices or locations
Auto-Load RolePath on SMS/Browser Sign-In
Given user U receives an SMS sign-in link for location L or resumes an authenticated browser session at L When U opens the link or session Then the system loads the correct RolePath within 3 seconds on a 4G connection using priority: Active Shift > User Assignment > Device Default And no mobile app install or additional hardware is required And the loaded RolePath determines the initial screen, hotkeys, and permissions presented
Comprehensive Audit Trail for RolePath Changes
Given any create, update, delete, or switch action affecting RolePath assignments or active roles When the action is performed via UI, API, shift automation, or device default Then an immutable audit record is stored with actor, target user, role(s), location, action type, source, timestamp (UTC), previous value, new value, and optional reason And authorized admins can filter by location, user, role, action type, and date range, and export results to CSV And audit records are retained for at least 12 months and are tamper-evident
Multi-Location Safeguards and Scope Enforcement
Given an operator manages multiple locations L1..Ln and user U is provisioned only at a subset When attempting to assign a RolePath for U at a location where U lacks access Then the system blocks the action with a clear error indicating missing location access And API endpoints enforce location scoping based on the caller's permissions And bulk assignment previews show the target locations and require explicit confirmation before applying And rate limiting prevents more than 100 role assignment updates per minute per operator account
Role‑Specific Screen Loader
"As a Curb Captain, I want the app to open directly to the Arrival Board with bay controls so that I can park cars and confirm handoffs fast."
Description

On session start, load only the screens, panels, and controls relevant to the assigned role, hiding non‑essential or disallowed features. Preconfigure layouts such as Arrival Board with bay controls for Curb Captain, Fire Queue and Pack‑Out for Expo‑Fire, and Dispatch Console for Ghost Kitchen. Ensure fast, low‑latency rendering on common browsers/tablets, numbered bay visibility, and touch/keyboard parity. Provide a preview mode so managers can verify a role’s layout before rollout. No app install required; operates entirely in the browser.

Acceptance Criteria
Role-specific layout loads on session start
Given a user with role "Curb Captain" authenticates via browser, When the session starts, Then only the "Arrival Board" with bay controls is loaded and non-role modules are excluded from the DOM and network requests. Given a user with role "Expo‑Fire" authenticates, When the session starts, Then "Fire Queue" and "Pack‑Out" panels render as the default view and non-permitted modules are not instantiated. Given a user with role "Ghost Kitchen Dispatcher" authenticates, When the session starts, Then the "Dispatch Console" renders as the default view with no non-permitted panels present. Given any supported device and a 10 Mbps connection, When the session starts, Then time to first interactive role UI is ≤ 2.0 s and the largest role panel is visually complete ≤ 2.5 s.
Non-role features are hidden and blocked
Given a user lacks permission for "BaySwap override", When they invoke the BaySwap hotkey, Then an inline "Not permitted" message appears and no state change occurs. Given the same user, When they enter the direct URL to a restricted panel, Then the app shows an Access Denied view (HTTP 403) and no restricted data is returned in network or console logs. Given the same user, When using navigation search/command palette, Then restricted actions do not appear in results. Given any role, When a feature flag disables a module, Then the module is neither visible nor reachable via URL or hotkey.
Preconfigured layouts per role
Given role "Curb Captain", When the layout loads, Then "Arrival Board" shows bay controls docked on the right with default bay order ascending and column widths per spec. Given role "Expo‑Fire", When the layout loads, Then "Fire Queue" is the primary tab and "Pack‑Out" is secondary, with auto-focus on the oldest fire ticket. Given role "Ghost Kitchen Dispatcher", When the layout loads, Then "Dispatch Console" opens with driver list panel visible and order filter defaulted to "Ready". Given an admin changes a user's role, When the user refreshes, Then the new role layout replaces the old one without stale panels or cached permissions.
Numbered bay visibility and control for Curb Captain
Given role "Curb Captain", When viewing Arrival Board, Then numbered bays 1..N are visible, editable labels (where enabled) are available, and unassigned cars can be assigned to a bay. Given the user assigns a car to a bay, When the assignment is saved, Then the change is reflected across connected clients within ≤ 1 s and persists on refresh. Given roles other than "Curb Captain", When viewing their default layout, Then bay numbers and bay assignment controls are hidden unless explicitly granted. Given two users attempt conflicting assignments to the same bay within 2 s, When both actions occur, Then the system resolves with last-write-wins and displays a non-blocking notice to both users.
Touch and keyboard parity across role UIs
Given a supported tablet (iPadOS/Android) and a desktop browser, When operating any primary control in the role layout, Then the action is performable via touch and via a documented keyboard shortcut. Given tab navigation, When moving through interactive elements, Then focus order is logical and every action has a visible focus state. Given the role hotkey reference is opened, When the user performs a listed shortcut, Then the corresponding UI action occurs with end-to-end latency ≤ 150 ms at the 95th percentile. Given a control is disabled for the role, When its shortcut is invoked, Then no state change occurs and an unobtrusive notification explains the restriction.
Manager preview mode for role layouts
Given a user with Manager permission, When selecting "Preview Role" and choosing a role, Then the selected layout renders with a persistent "Preview" banner and simulated data as configured. Given Preview mode, When an action would modify live orders, Then the system blocks it, shows "Preview only — no changes applied", and performs no write API calls. Given Preview mode is active, When the user exits, Then the app returns to the manager's own role layout with no lingering preview state or cookies. Given a deep link into a role panel, When opened in Preview, Then the linked panel opens correctly without affecting production metrics or notifications.
Low-latency browser-only rendering on common devices
Given supported environments (Chrome/Edge 114+, Safari iPadOS 16+, Firefox ESR) on mid-tier hardware, When a cold start loads the role layout over 10 Mbps, Then First Contentful Paint ≤ 1.2 s, Time to Interactive ≤ 2.0 s, and total JS payload for that role ≤ 500 KB gzipped. Given a warm start with cached assets, When reloading the same role layout, Then Time to Interactive ≤ 1.0 s and no cross-role modules are requested over the network. Given background tab throttling, When returning focus to the app, Then role panels resume within ≤ 300 ms with state intact and without a full reload. Given a clean device with no native app installed, When accessing via a supported browser, Then all role features operate without prompting for native installation or extensions. Given a 3 Mbps connection, When loading, Then the skeleton UI appears within ≤ 400 ms and critical role controls are usable within ≤ 3.5 s.
Role Hotkey Map & On‑screen Hints
"As an Expo, I want clear hotkeys and hints for firing and status updates so that I can keep working the line while updating orders quickly."
Description

Define and enforce a per‑role hotkey map for frequent actions (e.g., Assign Bay, BaySwap, Mark Delivered, Instant Adjust). Display contextual on‑screen hints and tooltips during training, with the option to keep hints visible in early live sessions. Detect and resolve hotkey conflicts, support hardware keyboards and touch equivalents, and localize labels. Provide an admin view to customize mappings by location while maintaining sensible system defaults.

Acceptance Criteria
Per-Role Hotkey Map Enforcement in Live Ops
Given a user is authenticated as a specific role with an active hotkey map When the user presses a mapped hotkey on a relevant screen Then the corresponding action executes and a visual confirmation appears within 150 ms Given the same session and screen When the user presses an unmapped or disallowed hotkey Then no action executes and a non-blocking hint “Not available for this role” appears within 300 ms Given role-based permissions are enforced When a hotkey targets an action outside the role’s permissions Then the action is blocked and the attempt is logged with userId, role, actionId, key, timestamp
Contextual On-Screen Hints During Training Mode
Given Training Mode is enabled for the user’s role When any hotkey-enabled screen is opened Then each actionable control displays a tooltip showing action label and hotkey (e.g., “Assign Bay — A”) within 500 ms And tooltips do not occlude primary controls and can be dismissed via Esc or tap X And tooltips are keyboard- and screen-reader accessible (focusable, aria-label includes action and key) And hints reflect the current role’s mapping and current locale
Persistent Hints for Early Live Sessions
Given “Keep hints visible” is enabled for the user with a configured limit of N sessions (default 3) When the user exits Training Mode and starts live session 1..N Then hotkey tooltips are visible by default on hotkey-enabled controls And the user can toggle hints off per session; the toggle state persists for that session only And after N sessions, hints default to off on subsequent sessions And an admin can adjust N per location (range 1–10)
Admin Conflict Detection and Auto-Resolution on Save
Given an admin edits a role’s hotkey map for a location When attempting to save with duplicate assignments, browser/OS-reserved combos, or screen-level collisions Then the save is blocked, conflicted fields are highlighted, and conflict reasons are shown inline And the system offers Auto-Resolve to reassign only conflicted actions to available keys without creating new conflicts And after Auto-Resolve or manual fixes, Save succeeds only when zero conflicts remain And all changes and conflict resolutions are audit-logged with adminId, timestamp, before/after
Hardware Keyboard Support and Touch Equivalents
Given a hardware keyboard is detected on a supported browser (Chrome, Safari, Edge, Firefox latest two versions) When the user presses a mapped hotkey Then the action triggers once per keypress without interfering with browser-reserved behaviors And key repeat triggers repeatable actions only when explicitly marked repeatable Given a touch-only device or no keyboard detected When the user interacts with a hotkey-enabled control Then a visible touch equivalent (e.g., long-press menu or action chip) is available and labeled with the same action text And touch targets meet a minimum 44x44 dp size and respond within 150 ms
Localized Hotkey Labels and Tooltips
Given a location language is set (e.g., en-US, es-ES, fr-FR, ar-SA) When hotkey labels and tooltips render Then all action labels are localized to the selected language and key names follow OS locale conventions (e.g., Cmd on macOS, Ctrl on Windows) And right-to-left languages render correctly with mirrored layout where applicable And if a translation is missing, the system falls back to en-US and records a missing-translation event
Admin Location-Level Customization with System Defaults
Given system default hotkey maps exist per role When an admin opens the Role Hotkey Map for a specific location Then defaults are displayed, overrides are clearly indicated, and an Inherit vs Override toggle is available per action And the admin can reset any overridden action back to default and preview the full map before publishing And publishing applies changes to active users within 60 seconds or next screen load, whichever comes first And an audit log records editor, role, location, changes, and publish timestamp
Progressive Training Flow & Simulation Mode
"As a new Dispatcher, I want step‑by‑step scenarios that mirror rush conditions so that I can gain confidence before handling live orders."
Description

Deliver guided, role‑specific modules that progress from basics to advanced moves (including BaySwap overrides and Instant Adjust). Provide a high‑fidelity simulation mode that generates realistic arrivals, misparked cars, multi‑guest queues, and timing pressure without touching live orders. Gate advancement with checkpoints, measure time‑to‑complete and accuracy, and allow resuming mid‑module. Enable one‑click switch between Simulation and Live, with clear visual separation to prevent mistakes.

Acceptance Criteria
Role-Specific Track Loading
Given an authenticated user with role assignment (Curb Captain, Expo‑Fire, Ghost Kitchen Dispatcher), When the user starts training, Then only the modules, UI panels, and decision points mapped to that role are displayed. And When the user attempts to navigate to a screen not mapped to the role’s training track, Then access is blocked with a clear message "Not in this track" and no content is rendered. And When the user changes their role and restarts training, Then the loaded track updates to the new role without showing prior role content. And The track name and version are visible on the module intro screen. And Hotkey hints display only the hotkeys defined for the current role.
Checkpoint-Gated Progression
Given a module with defined checkpoints, When a trainee completes a checkpoint with a score that meets or exceeds the configured pass threshold and all required actions are performed, Then the next section unlocks. And When a trainee fails a checkpoint (score below threshold) or exceeds the time limit, Then the next section remains locked and a retry option is presented. And When a trainee attempts to deep link to a locked section, Then the system returns a Locked state and does not render section content. And The pass threshold and time limit are configurable per module and persisted. And Completion time and number of attempts per checkpoint are recorded for reporting.
High-Fidelity Simulation Without Live Impact
Given Simulation Mode is active, When the scenario runs, Then the system generates synthetic arrivals, misparked cars, and multi‑guest queues using a seeded randomizer. And All simulated entities are tagged sim=true and stored/read from a sandbox context. And No live orders, bays, SMS messages, inventory, or third‑party systems are modified or notified during Simulation Mode. And Within a 5‑minute run using the default "Lunch Rush" profile, at least one instance of each event type (arrival, misparked, multi‑guest queue) occurs. And Simulation parameters (rates, concurrency, random seed) are configurable per run and auditable.
One-Click Sim/Live Switch with Visual Separation
Given a user is viewing training, When the user activates the Sim/Live toggle (click or hotkey), Then the mode switches within 1 second and the current view refreshes to the selected mode. And When in Simulation Mode, Then a persistent SIMULATION watermark, distinct color theme, and "/sim" URL segment are visible on all screens. And When switching from Simulation to Live while training is in progress, Then a confirmation dialog requires explicit confirmation before proceeding. And When in Simulation Mode, Then actions that would alter live data are blocked and clearly labeled as simulated; no live side effects occur.
Resume Mid-Module with State Restoration
Given a trainee exits mid‑module (tab close, network loss, logout), When the trainee returns and chooses Resume, Then the last completed step, checkpoint status, and in‑step state are restored. And Time‑on‑task excludes time while the trainee was away and resumes from the last saved timestamp. And Auto‑save occurs at each step boundary and at least every 15 seconds during active interaction. And The resumed module loads to an interactive state within 2 seconds on a median connection.
Time-to-Complete and Accuracy Metrics
Given a trainee completes a module, When metrics are computed, Then time‑to‑complete and per‑checkpoint accuracy (%) are persisted to the trainee’s record. And An authorized user can view metrics per trainee and aggregated by role and module with a selectable date range. And Metrics can be exported as CSV via the UI and retrieved via an API endpoint. And Accuracy is calculated as correct required actions divided by total required actions per checkpoint, rounded to the nearest percent.
Advanced Moves: BaySwap Override & Instant Adjust
Given an advanced training module, When the trainee executes a BaySwap Override scenario, Then the flow includes conflict detection, confirmation, and outcome review, and the attempt is graded for correctness and completion time. And When the trainee performs an Instant Adjust scenario, Then validations, limits, and permission checks are simulated and graded. And If the trainee’s production role lacks the corresponding live permission, Then the action remains available in Simulation but is clearly labeled "Training Only" and cannot affect Live mode. And Successful completion of both advanced scenarios is required to mark the advanced module complete.
Permission Guardrails & Safe Overrides
"As a manager, I want guardrails with supervised overrides so that trainees can’t make risky changes on live orders without approval."
Description

Enforce role‑based permissions to reduce mistakes, restricting sensitive actions by default. Implement supervised override flows for advanced actions (e.g., BaySwap override, Instant Adjust) using manager PIN/SMS approval, with clear warnings and post‑action audit logs. Provide undo/revert within a safe window and highlight impacted orders and bays before confirmation. Align permissions with existing CurbPing RBAC and ensure visuals clearly communicate when an action is restricted or escalated.

Acceptance Criteria
Role-Based Restriction Enforcement (Aligned with RBAC)
Given the RBAC configuration restricts Instant Adjust and BaySwap override for role "Curb Captain" And a user is signed in as "Curb Captain" When the user attempts to perform Instant Adjust or BaySwap override Then the action is blocked with a message indicating "Restricted: manager approval required" And no order, bay, or ticket state is changed And a denied-attempt event is recorded with userId, role, action, orderId/bayId, timestamp And if RBAC is updated to allow the action for the role, the permission change takes effect within 60 seconds without a page reload
Visual Indicators for Restricted and Escalated Actions
Given a user lacks permission to perform an advanced action When viewing the action control in the UI Then the control is visually disabled with a lock icon and tooltip "Requires manager approval" And attempting to activate the control opens the Request Override flow instead of executing the action And after an approved override, the affected order card and action menu display an "Escalated" badge with approver name within 2 seconds
Manager PIN Override Flow
Given a user without permission initiates Request Override and selects Manager PIN When the system prompts for a manager PIN and displays a risk summary of the action Then entering a valid manager PIN authorized for that action within 90 seconds executes the action And entering an invalid PIN three times locks the override flow for 2 minutes and keeps the action blocked And the audit log records approverId, method=PIN, decision=approved/denied, and response time
SMS Approval Override Flow
Given at least one on-duty manager has a verified mobile number And a user without permission initiates Request Override and selects SMS Approval When the system sends a one-time approval link containing action summary and expiration Then an approval received within 2 minutes executes the action and confirms to the requester And a denial or expiration blocks the action and notifies the requester with the reason And the audit log records approverId, method=SMS, decision, and response time
Pre-Confirmation Impact Preview and Warnings
Given a restricted action would affect specific orders and bays When the user initiates the action Then a modal lists impacted orderIds and bay numbers with counts and warnings (e.g., conflicts, ETA impact) And the confirm button remains disabled until the user acknowledges the warnings via a checkbox And closing the modal or canceling results in no changes to system state
Undo/Revert Safe Window
Given an advanced action has been executed via override Then an Undo control is displayed for 120 seconds or until the order is completed, whichever comes first And activating Undo restores the prior order assignments, bay, and timers atomically And the audit log appends a revert entry linked to the original action And Undo is unavailable after the window expires and no partial state persists
Audit Logging and Traceability
Given any attempt or completion of a restricted action (blocked, approved, or reverted) Then an audit record is created within 10 seconds including action, status, actorId, role, approverId (if any), method (PIN/SMS), before/after state snapshot, timestamp (ISO 8601 UTC), and correlationId And audit records are immutable and queryable in the Staff Activity Log by orderId and time range And export of audit records includes the same fields without loss or redaction for authorized roles
Training Progress Analytics & Certification
"As an owner, I want visibility into who is certified on each role and where they struggle so that I can staff wisely and target coaching."
Description

Track per‑user progress across modules: completion status, accuracy, error types, average action times, and scenario performance. Provide dashboards for managers with filters by role, location, and time range; export CSV; and send SMS/email nudges for overdue training. Define certification criteria per role and auto‑certify when thresholds are met, logging certification dates and expirations. Surface insights (e.g., common BaySwap errors) to inform coaching and process tweaks.

Acceptance Criteria
Manager filters dashboard by Role, Location, and Time Range
Given I am a manager with analytics access When I open Training Progress Analytics Then I see filters for Role, Location, and Time Range with defaults of All Roles, My Locations, Last 30 Days Given I set Role=Expo‑Fire, Location=Downtown, Time Range=This Week When I apply filters Then the dashboard updates within 2 seconds (p95) and shows only users and metrics matching those filters And the active filters are displayed as chips Given I clear all filters When I confirm Then the view resets to defaults and metrics recalculate accordingly Given filters produce no matching records When the view renders Then a zero-state message is shown and Export CSV is disabled
Per-user Training Metrics Visibility and Accuracy
Given a known test dataset is loaded When I view the per-user list Then each row displays: Module Completion Status (Not Started/In Progress/Completed), Accuracy %, Top 3 Error Types, Average Action Time (s), Scenario Pass Rate % And Accuracy % = (correct answers / total answers) within the selected time range, rounded to nearest 1% And Average Action Time (s) = mean time from prompt to correct action within the selected time range, rounded to 0.1s And Top 3 Error Types are ranked by frequency within the selected time range And Scenario Pass Rate % = (passed scenarios / attempted scenarios) within the selected time range, rounded to nearest 1% And displayed values match the expected results for the test dataset
Export Filtered Analytics to CSV
Given Role, Location, and Time Range filters are applied and I have permission for the selected locations When I click Export CSV Then a UTF-8 encoded CSV is generated within 10 seconds containing only filtered data And the CSV includes a header row with columns: User ID, Name, Role, Location, Module, Completion Status, Accuracy %, Average Action Time (s), Scenario Pass Rate %, Top Error Types, Training DateTime (ISO 8601 UTC), Certification Status, Certification Issued, Certification Expires And the number of data rows equals the count of records displayed for the selected time range And values that could be interpreted as spreadsheet formulas are safely escaped And the file opens without encoding errors in Excel and Google Sheets
Automated SMS/Email Nudges for Overdue Training
Given a trainee has one or more overdue modules per their assigned RolePath track and has a valid SMS number and/or email When the daily nudge job runs at 09:00 local time Then the trainee receives a nudge via available channels including: list of overdue modules, due date(s), and a resume-training link And no more than one nudge per user is sent in any 24-hour period, with a maximum of three nudges per overdue episode And users opted out of SMS or email do not receive nudges on those channels And each nudge is logged with timestamp, channel, delivery status, and message ID visible to managers And when all overdue modules are completed, no further nudges are sent
Role-Based Auto-Certification and Expiration Logging
Given certification thresholds are defined for a role (required modules completed, minimum Accuracy %, maximum Average Action Time, minimum Scenario Pass Rate) When a trainee meets all configured thresholds based on their training records Then the system automatically marks the trainee as Certified for that role And a certification record is created with: Role, Issued Date (UTC), Expiration Date (per role policy), and the metrics snapshot used to certify And the trainee’s certification status, issued date, and expiration date appear on the manager dashboard and in CSV export And when a certification reaches its expiration date, the status changes to Expired at 00:00 local time and the event is logged
Insight Surfacing for Common Error Patterns
Given error events are recorded across trainees When I open Insights with Role, Location, and Time Range filters applied Then I see up to five insights ranked by impact (e.g., “BaySwap errors: 28% of errors, +12% WoW”) And each insight shows: error type, frequency %, trend versus prior period, top affected modules, and top five impacted users And clicking an insight filters the dashboard to relevant users and modules And the counts and trends match expected values for a seeded dataset
Data Freshness and Performance SLOs
Given new training events occur (module completion, scenario attempt, error tagged) When I view the dashboard Then metrics reflect those events within 5 minutes (p95) and a “Last updated” timestamp is shown And initial dashboard load completes in under 2.5 seconds (p95) for up to 2,000 active trainees and 12 months of data And applying or clearing filters completes in under 1.5 seconds (p95) And the UI shows a loading state and retries on transient errors

ScriptCoach Overlay

Adaptive on‑screen guidance that suggests the precise quick reply, tone, and accessibility phrasing for each drill step. Trainees preview what guests see via SMS, learn the ‘why’ behind messages, and practice clear, dignified communication that speeds handoffs and boosts satisfaction.

Requirements

Contextual Quick Reply Suggestions
"As a trainee curbside lead, I want context-aware quick reply suggestions so that I can respond faster and more consistently during handoffs."
Description

Provide adaptive, on-screen SMS quick reply suggestions at each ScriptCoach drill step (e.g., arrival confirmation, bay assignment, delay communication) that auto-personalize with live order context from CurbPing (bay number, pickup code, ETA) without exposing PII. Offer one-tap insertion, placeholder autofill, and tone variants (concise, friendly, formal) while enforcing SMS best practices such as character limits and segment awareness. Integrate within the training overlay and sandbox using simulated orders, log selections for coaching, and support keyboard-only operation for accessibility.

Acceptance Criteria
Auto-Personalized Quick Replies Without PII
Given a ScriptCoach drill step is active and live order context {bayNumber, pickupCode, ETA} is available, when quick reply suggestions render, then each suggestion auto-populates those values without exposing guest name, phone number, or vehicle plate. Given any context field is missing, when suggestions render, then the missing field is omitted or replaced with a non-PII fallback phrase and no placeholders like {{...}} remain visible. Given suggestions are displayed, when inspecting UI and network payloads, then no PII (names, phone numbers, emails, vehicle plates) is present and values are masked per policy where applicable. Given a drill step change, when moving to the next step, then suggestions refresh with the new step's context within 300 ms.
One-Tap Insertion with Placeholder Autofill
Given a suggestion is visible, when the user clicks it or presses Enter on the focused suggestion, then its text inserts into the SMS compose field within 150 ms. Given the suggestion text contains placeholders, when inserted, then all placeholders resolve with current context values and no unresolved tokens remain. Given text is inserted, when the user continues typing, then the cursor is positioned at the end of the inserted text and undo (Ctrl+Z/Cmd+Z) reverts the insertion. Given a suggestion is inserted, when the order context changes before send, then the user is prompted to refresh values rather than silently altering the composed text.
Tone Variants and SMS Preview
Given a drill step is active, when the user switches tone between Concise, Friendly, and Formal, then the suggestion text updates accordingly and the selected tone persists for the session. Given a tone is selected, when previewing, then the SMS preview shows exactly what the guest would receive with current context values applied. Given tone is changed, when comparing variants, then each variant differs in wording by at least 10 characters to ensure meaningful variation. Given the preview is visible, when the user requests guidance, then a brief rationale (<=140 characters) explaining the phrasing is shown for the selected tone.
Character Limit and Segment Awareness
Given a suggestion is selected, when counting characters using GSM-7/Unicode detection, then real-time character and segment counts display accurately for the composed message. Given the composed message would exceed 2 segments, when inserting or editing, then the UI blocks send/insertion, displays a warning, and offers a one-click shorter variant that fits in <=2 segments. Given the message contains Unicode characters increasing segment count, when the user chooses Optimize, then safe substitutions are suggested to reduce segment count where possible.
Training Sandbox with Simulated Orders
Given sandbox mode is active, when loading, then a simulated order with bayNumber, pickupCode, and ETA is generated without any PII. Given the user advances through drill steps, when requesting new scenarios, then at least three distinct simulation profiles rotate (e.g., no-bay-yet, delayed-prep, curbside-full). Given the user clicks Reset Simulation, when confirmed, then a new simulated order replaces the current one and all context-dependent suggestions update within 300 ms.
Selection Logging for Coaching
Given suggestions are displayed, when a suggestion is viewed, highlighted, or inserted, then an analytics event is captured with templateId, tone, stepId, timestamp, and sessionId but not the free-text body. Given a training session ends, when exporting logs, then a CSV is available containing the captured fields and aggregate counts per step. Given a user requests data deletion, when processed, then all associated training events are erased within 24 hours and the deletion is recorded in an audit log.
Keyboard-Only Accessibility
Given keyboard-only navigation, when tabbing through the overlay, then all actionable elements are reachable in a logical order with visible focus indicators. Given a suggestion has focus, when pressing Enter then it inserts; when pressing Space then it previews; when using Arrow keys then selection changes; when pressing 1/2/3 then tone switches. Given a screen reader is active, when navigating the overlay, then all suggestions and controls announce meaningful labels and states and the overlay conforms to WCAG 2.1 AA criteria 2.1.1, 2.4.3, 2.4.7, and 4.1.2.
Tone and Accessibility Coach
"As a trainee, I want guidance on tone and accessible phrasing so that every guest message is clear, inclusive, and easy to understand."
Description

Deliver real-time guidance that flags jargon, reading level, and potential accessibility issues in drafted replies, and suggests inclusive, plain-language alternatives that align with brand voice. Include checks for SMS readability (avoid all caps, clear numerals, simple syntax), bilingual/localized phrasing where configured, and safe emoji usage. Provide optional accessibility hints for MMS content (e.g., alt text), maintain WCAG-aligned on-screen cues in the overlay, and ensure no sensitive or personal data is stored in coaching logs.

Acceptance Criteria
Real-Time Tone and Accessibility Flagging
Given a trainee drafts a reply in the ScriptCoach overlay When the draft includes jargon from the configured list or exceeds the configured reading level threshold (default Grade 6) Then the coach highlights each offending phrase inline, labels the issue type (e.g., Jargon, Reading Level), and displays a brief rationale And suggests at least one plain-language alternative per issue aligned to the active brand voice profile And updates indicators and suggestions within 300 ms after the last keystroke for edits under 200 characters And when all issues are resolved, all indicators clear within 300 ms and the draft is marked Compliant
SMS Readability and Safe Emoji Checks
Given a trainee composes an SMS reply When the message contains two or more consecutive ALL-CAPS words or more than three repeated punctuation marks (e.g., !!!) Then the coach flags a tone/readability risk and suggests a lowercase/plain punctuation alternative When ambiguous numerals or formats are detected (e.g., O vs 0, dates/times not in locale format) Then the coach suggests clear numerals and locale-appropriate date/time formatting When emojis not in the configured safe set or more than two emojis are used Then the coach flags the issue and suggests text equivalents or reduces emoji count And a visible SMS Readability badge displays Pass only when all checks are resolved for the current draft
Bilingual and Localized Phrasing Suggestions
Given a location has a secondary language enabled and a brand glossary configured When the customer language preference is known Then the coach offers a one-click translation that preserves brand voice and uses glossary terms, with a side-by-side preview When the customer language preference is unknown Then the coach suggests a concise bilingual template within the configured character limit for SMS (e.g., 160 chars where applicable) When the draft includes idioms or region-specific terms not in the glossary Then the coach flags potential mistranslation risk and proposes glossary-backed alternatives
MMS Accessibility Hints and Alt Text
Given a trainee attaches an image or rich media to a message When no alt text is provided Then the coach prompts for alt text with guidance (5–120 characters, objective description, no PII) and offers an auto-suggested draft for confirmation When embedded text is detected in the image via OCR Then the coach recommends including critical visible text in the alt description When alt text exceeds 120 characters or includes all-caps/emojis Then the coach flags and suggests a concise, plain-language revision And when alt text meets guidance, the MMS Accessibility badge displays Pass
WCAG-Aligned Overlay Compliance
Given the ScriptCoach overlay is open When navigating with keyboard only Then all interactive elements are reachable in a logical order with visible focus indicators and no focus traps When checking visual contrast Then body text and indicators meet or exceed 4.5:1 contrast (icons/non-text 3:1) When assistive technologies are used Then status messages and validation hints are announced via ARIA live regions and controls have accessible names When reduced-motion is enabled at OS/browser level Then non-essential animations are disabled and transitions are minimized And suggestion popovers do not obscure primary actions (e.g., Send) at common viewports (≥320px width)
Privacy and Data Minimization in Coaching Logs
Given coaching guidance is generated for a draft When logs or telemetry are written Then no message content, phone numbers, customer names, or other PII/PHI are persisted; only aggregate, non-identifying counters and timings are stored When server/application logs are queried for the session Then zero PII-bearing fields are present and data retention for coaching telemetry is set to 0 days at rest (ephemeral processing only) When exporting analytics Then outputs contain only aggregated metrics (e.g., issue type counts) with k-anonymity (k ≥ 10) applied
Performance and Non-Blocking Guidance
Given the trainee is typing in the overlay When suggestions are generated client-side and/or via service calls Then median suggestion latency is ≤150 ms and p95 ≤400 ms for drafts up to 200 characters on reference hardware/network When the trainee presses Send Then message sending is not blocked by coaching computations or network calls; guidance updates continue asynchronously When the network is unavailable Then local checks continue to function, remote-dependent checks degrade gracefully with a visible Offline badge, and no errors prevent drafting or sending
Live SMS Guest Preview
"As a trainee, I want to preview exactly how my SMS will appear to guests so that I can catch issues before sending."
Description

Render a side-by-side preview that mirrors common SMS client display, including line breaks, link presentation for CurbPing’s 'I’m here' and bay/map links, and tap targets. Show character count and segment estimator (GSM vs. Unicode) with warnings when messages exceed thresholds. Allow toggles for light/dark mode, large text, and language so trainees can validate clarity before sending. Ensure keyboard navigation and screen reader compatibility within the overlay.

Acceptance Criteria
Accurate SMS Layout Mirror (Links, Line Breaks, Tap Targets)
Given a composed message with explicit line breaks, When the preview renders, Then line breaks appear in the same positions in the SMS preview. Given the message includes CurbPing "I'm here" and bay/map URLs, When the preview renders, Then those URLs are auto-detected and styled as tappable links consistent with common SMS clients, without triggering navigation inside the preview. Given link elements in the preview, When measured, Then each tappable target is at least 44x44 px and shows distinct hover and keyboard focus states. Given plain text that is not a URL, When rendered, Then it is not styled as a link or tappable target. Given the preview is displayed side-by-side with the composer, When the overlay is open, Then both panes remain aligned with no overlap or clipping at standard desktop widths (≥1280px).
Real-time Character Count & Segment Estimation (GSM vs Unicode)
Given the trainee types only GSM-7 characters, When text changes, Then the counter updates and the estimator shows encoding "GSM-7" with segments calculated as 160 chars for the first segment and 153 for each additional segment. Given the message contains GSM-7 extended characters (e.g., ^ { } \ [ ] ~ | €), When text changes, Then each extended character is counted as two septets in the estimator. Given the message contains any non-GSM Unicode characters (e.g., emoji), When text changes, Then the estimator switches to "Unicode" with segments calculated as 70 chars for the first segment and 67 for each additional segment. Given the trainee pastes or deletes content, When text changes, Then the counter and estimator refresh within 200 ms. Given the composer is empty, Then the counter shows 0 and the estimator shows 0 segments.
Threshold Warnings for Message Length
Given default thresholds (soft warning at >1 segment, hard warning at >3 segments), When the estimator calculates segments, Then a soft warning appears for 2–3 segments and a hard warning appears for 4+ segments. Given a soft warning is shown, When the trainee edits the message back to 1 segment, Then the soft warning disappears immediately. Given a hard warning is shown, When the trainee reduces the message to ≤3 segments, Then the hard warning downgrades or clears accordingly. Given warnings are displayed, Then they do not obscure the previewed SMS content and are announced to assistive technologies. Given warnings are displayed, Then they include the current character count and segment count values.
Mode Toggles: Light/Dark & Large Text
Given the trainee enables Dark Mode, When the preview renders, Then colors switch to a dark palette and maintain contrast ratios of ≥4.5:1 for body text and ≥3:1 for large text and UI elements. Given Dark Mode is disabled, When toggled off, Then the preview reverts to Light Mode without altering the composed message. Given the trainee enables Large Text, When the preview renders, Then SMS text size increases by at least 30% with wrapping and line breaks accurately reflected; tappable link targets scale to remain ≥44x44 px. Given Large Text is enabled, When the character counter and estimator are displayed, Then they remain legible and do not overlap other UI. Given any combination of Light/Dark and Large Text is toggled, When applied, Then the preview remains functional and visually consistent.
Language Toggle and RTL Rendering Support
Given the trainee selects a language from the language toggle, When the preview renders, Then the preview applies locale-appropriate typography and punctuation shaping without altering the underlying message text. Given an RTL language is selected, When the preview renders, Then text direction switches to right-to-left, alignment adjusts accordingly, and links remain visually identifiable and keyboard-focusable. Given an LTR language is selected, When the preview renders, Then text direction remains left-to-right and link styling is preserved. Given the language is changed while composing, When the estimator recalculates, Then encoding and segment counts reflect the actual characters present (GSM-7 vs Unicode) regardless of locale. Given the language toggle is changed repeatedly, When applied, Then no duplicate or stale content appears in the preview.
Accessibility: Keyboard Navigation and Screen Reader Compatibility
Given focus enters the overlay, When navigating with Tab/Shift+Tab, Then all controls (light/dark toggle, large text toggle, language selector, close) are reachable in a logical order with visible focus indicators. Given a keyboard-only user, When interacting with controls, Then Space/Enter activates toggles and no keyboard traps occur; pressing Esc closes the overlay. Given a screen reader user, When navigating the preview, Then the preview region has an accessible name and role and does not continuously announce on each keystroke. Given the character counter and segment estimator, When focused by a screen reader, Then their current values are announced and update coherently as content changes. Given color contrast is evaluated, When measured, Then all text and interactive controls meet WCAG 2.1 AA contrast requirements.
Inline Rationale Tooltips
"As a trainee, I want to see the reason behind each suggested message so that I learn when and why to use it instead of memorizing scripts."
Description

Attach concise, non-blocking tooltips to each suggested reply that explain the underlying rationale (e.g., leading with pickup code reduces confusion, repeating bay number cuts misparks, setting a time window manages expectations). Provide micro-guidelines like ideal length, ordering of key details, and when to use a template variant. Tooltips are accessible via hover, focus, or tap, are dismissible, and link to deeper guidance in the training knowledge base.

Acceptance Criteria
Reveal tooltip on hover, focus, and tap
Given I am viewing a drill step with suggested replies And a rationale icon/badge is present next to each suggested reply When I hover the icon with a pointer Then the tooltip appears within 200ms and adjacent layout shifts by no more than 2px When I focus the icon via keyboard (Tab/Shift+Tab) Then the tooltip appears and focus remains on the trigger element When I tap the icon on a touch device Then the tooltip appears positioned adjacent to the icon and does not obscure the trigger
Rationale and micro-guidelines content
Given a suggested reply has an associated tooltip When the tooltip opens Then it displays a concise rationale sentence (<= 180 characters) explaining the why behind the reply And it displays 2–3 micro-guidelines including: ideal reply length target (e.g., 90–160 characters), required ordering of key details, and when to use a template variant And total tooltip text does not exceed 400 characters And content is plain text except for the single Learn more link And the content dynamically reflects the current reply context (e.g., bay assigned vs. not assigned)
Knowledge base deep link
Given a tooltip is open When I activate the Learn more link Then a new tab/window opens to the specific knowledge base article for the current step and template And the URL includes stepId and templateId query parameters for deep-linking And the request returns HTTP 200 within 3 seconds If the KB returns an error or times out Then the UI shows an inline 'Article unavailable' message without closing the tooltip And the link is keyboard-focusable and announced with an accessible name 'Learn more about this reply'
Dismissal and non-blocking behavior
Given a tooltip is open When I press Escape OR click/tap outside the tooltip OR activate its Close button Then the tooltip closes within 100ms And the Close button is visible, has aria-label 'Close rationale', and is reachable via keyboard And no overlay intercepts pointer events outside the tooltip And the tooltip auto-repositions if it would overlap critical controls (e.g., Send/Apply/Next), ensuring those controls remain clickable
Accessibility compliance (WCAG AA)
Given a user navigates with a keyboard or screen reader When focusing the tooltip trigger Then the trigger is focusable and has an accessible name describing its purpose (e.g., 'Why this reply') And the tooltip uses role='tooltip' and is associated to the trigger via aria-describedby or aria-controls And screen readers announce the tooltip content once without trapping focus And all text/iconography in the tooltip meets 4.5:1 contrast ratio (WCAG AA) And interactive targets (trigger and Close) are at least 44x44 CSS pixels And the tooltip remains usable at 200% zoom without clipping or loss of functionality
Viewport-aware positioning and performance
Given devices from 320px to 1920px width When the tooltip opens near viewport edges Then it flips or repositions to remain fully visible with at least 8px margin from the edges And an arrow/connector points to the trigger And max width is 320px on mobile and 400px on desktop And the tooltip never exceeds 40% of viewport height and becomes scrollable if content overflows And opening/closing animations render at >= 50 FPS on a mid-tier device And first paint of tooltip content occurs within 200ms of trigger
Attachment coverage and state management
Given a drill step with N suggested replies (N >= 1) When the step renders Then each suggested reply displays exactly one tooltip trigger and non-reply elements do not When I open a tooltip for one reply Then any other open tooltip in the same step closes automatically When the suggested reply text or variant changes Then the tooltip content updates within 250ms to match the new context without requiring a page reload
Practice Mode with Scenario Playback
"As an operations trainer, I want a practice mode with realistic scenarios and scoring so that staff can build skill before going live."
Description

Offer a scripted practice environment that simulates end-to-end curbside flows—guest arrival via 'I’m here' link, bay reassignment, delays, misparked vehicles—where trainees respond using ScriptCoach. Automatically evaluate messages for correctness (bay and code present), clarity, timing, and accessibility criteria, then provide per-step feedback, scoring, and replay. Use anonymized, simulated data separate from live operations, and track completion for certification.

Acceptance Criteria
Simulated 'I’m here' Arrival and Bay Assignment Playback
Given a trainee launches Practice Mode and selects the "Standard Arrival" scenario When the simulated guest taps the 'I’m here' link Then a bay is assigned and displayed within 2 seconds and the playback timeline begins at T0 Given the arrival event is visible When the trainee sends the first reply using ScriptCoach Then the reply is sent within 15 seconds of T0, includes the resolved bay number and pickup code, and meets clarity >= 80 and accessibility >= 90 Given a reply is composed When the trainee previews the message Then the preview exactly matches the SMS content to be sent, including all resolved dynamic fields Given Practice Mode is active When any reply is sent Then no external SMS/webhook is triggered and the action is logged to the training audit with trainee ID and scenario ID
Bay Reassignment During Active Pickup
Given an active practice session with an assigned bay When a simulated bay reassignment event occurs mid-flow Then ScriptCoach surfaces an update template within 1 second indicating the new bay and rationale Given the reassignment event When the trainee replies Then the message is sent within 20 seconds, contains the new bay number, a clear action (e.g., "Please move to Bay 5"), and an apology phrase, and passes clarity >= 80 and accessibility >= 90 Given the update is sent Then the evaluation engine extracts the new bay correctly and awards full credit; if the old bay is stated as the destination, the step is marked incorrect
Handling Reported Delay with Accessible Messaging
Given a simulated guest message of "Running 10 late" (or equivalent) When the trainee responds Then the response is sent within 30 seconds, acknowledges the delay, provides an updated holding window or ETA, and offers at least one option (wait, reschedule, or cancel) Given ScriptCoach tone guidance is available When the trainee selects a tone Then a non-blaming, respectful tone tag is applied and the message meets clarity >= 80 and accessibility >= 90 Given the response is composed When preview is shown Then the preview matches the message content exactly
Misparked Vehicle Detection and Correction
Given a simulated misparked vehicle signal (guest reports wrong bay or staff mismatch) When the trainee responds Then the message is sent within 30 seconds, specifies the correct bay and a clear landmark or directional cue, includes a confirmation request (e.g., "Reply DONE when parked"), and meets clarity >= 80 and accessibility >= 90 Given the correction is sent When the simulated guest confirms Then the session advances to Located state and records the resolution time for scoring
Automated Evaluation, Feedback, Scoring, and Replay
Given any practice scenario When the trainee completes each step Then the system evaluates that step for correctness (bay and code present when required), timing against thresholds, and clarity/accessibility scores, and displays feedback within 1 second of step completion Given all steps are finished When the final score is computed Then a weighted score is shown with a per-step breakdown and flagged critical failures (missing bay or code causes step failure regardless of score) Given a completed run When the trainee selects Replay Then the session replays with original timestamps, events, and message overlays in the recorded order
Training Data Isolation and Certification Tracking
Given Practice Mode is active Then only anonymized sample data is used (guest name, phone, plate), no records are created in live operations queues, and no external SMS or webhooks are fired Given a trainee achieves final score >= 85 with no critical step failures When the run completes Then a certification record is created with trainee ID, org ID, scenario version, timestamps, and final score Given certification exists When an admin retrieves training records via UI or API Then the record is returned within 2 seconds and includes a replay link and audit log reference
Manager Configuration and Coaching Analytics
"As a manager, I want to configure our brand scripts and review coaching analytics so that training aligns with our standards and improves over time."
Description

Provide an admin console for managers to author drills, approve phrases, define brand tone rules, required elements (bay number, pickup code), language support, and accessibility constraints. Configure scoring weights and pass thresholds. Present analytics on trainee progress, common errors (e.g., missing bay), message length issues, time-to-first-reply, and improvement over time, with filters by location and role and CSV export. Integrate with CurbPing roles/permissions and adhere to data retention and privacy policies.

Acceptance Criteria
Drill authoring with required elements, tone, accessibility, and language support
Given I am a Manager with Admin Console access When I create a new drill with at least one step And I define required elements including "bay number" and "pickup code" And I set brand tone rules and accessibility constraints And I select supported languages (e.g., English, Spanish) and a default language Then the drill saves with a unique version and validation errors display if any required field is missing And the rules and supported languages are persisted and visible in drill settings And per-language content can be authored and previewed for each supported language And these configurations are available to the scoring and analytics engines for that drill
Phrase approval workflow and versioning
Given a draft phrase exists for a drill step When a Manager marks the phrase as Approved Then the phrase status updates to Approved with approver ID and timestamp recorded in the audit log And the Approved phrase is available in ScriptCoach suggestions within 60 seconds of approval And prior phrase versions remain accessible, and a rollback records who reverted, when, and the reason
Scoring weights and pass thresholds configuration
Given I open Scoring Settings for a drill When I assign numeric weights (0–100) for Required Elements, Tone Adherence, Message Length, and Time-to-First-Reply And I set a Pass Threshold (0–100) Then the UI prevents save unless total weights equal 100 and threshold is within range And completed training sessions compute a score using these weights And sessions display Pass/Fail against the configured threshold And changes to weights/threshold create a new scoring version referenced by subsequent sessions
Analytics dashboard with progress, errors, message length, and trends
Given training sessions exist across multiple locations and roles When I view Analytics with date range, location, and role filters applied Then the dashboard shows: trainee progress (assigned/started/completed, average score), common errors (including missing bay number counts and rates per 100 messages), message length distribution with % over configured limit, median and P90 time-to-first-reply, and weekly improvement trend lines And all displayed metrics update within 2 seconds of changing any filter And hovering a metric reveals its definition and calculation window
CSV export of filtered analytics with privacy safeguards
Given I have applied Analytics filters and a date range When I click Export CSV Then the exported file contains only records within the selected filters and date range And includes the columns: location_id, role, trainee_id, drill_id, session_date, score, pass, missing_bay_count, msg_length_avg, ttf_reply_ms_p50, ttf_reply_ms_p90, language And excludes restricted personal data per policy (e.g., phone numbers, raw message bodies) And the export action is logged with user ID, timestamp, and filter summary And the file is generated within 30 seconds for up to 100k rows
Roles and permissions enforcement for Admin Console
Given CurbPing roles and location assignments exist When a user without Manager or Admin role attempts to access the Admin Console Then access is denied with HTTP 403 and an explanatory UI message And users with Manager role can create/edit drills and view analytics only for their assigned locations And Admin users can manage global settings (e.g., retention period, scoring defaults) and all locations And all admin actions are captured in an audit log with user, action, entity, and timestamp
Data retention and privacy compliance for training data
Given a global retention period is configured (e.g., 180 days) When training data exceeds the retention period Then records are automatically purged within 24 hours and excluded from Analytics and exports And a Manager or Admin can request deletion of a specific trainee's data, which completes within 48 hours And all training data is encrypted at rest and in transit And access to training data is restricted to authorized roles and recorded in access logs

Mistake Replay

Instant, tappable replay after every drill with a timeline of actions, click‑slow heatmaps, and an ideal path comparison. Shows the projected impact on dwell time, reroutes, and tip conversion, then offers a one‑tap ‘Do Over’ to cement the fix through repetition.

Requirements

Drill Mode Session Capture
"As an ops manager, I want every training drill to automatically capture a complete, timestamped trace of actions so that I can pinpoint where workflows break down without screen recording or manual notes."
Description

Enable a drill mode that automatically records every staff interaction in the CurbPing Ops Console and related SMS events, including clicks, field focus, keystroke latency buckets (no raw text), state changes, API calls, and timestamps. Map low-level events to domain actions (e.g., check-in received, bay assigned, handoff confirmed) and persist a compact, replayable session object tied to a training sandbox order, not production. Respect privacy by redacting PII in SMS or notes, and constrain retention via configurable policies. Must work on mobile browsers and low-end devices with negligible performance impact (<5% overhead). Provide secure, shareable session IDs for coaching and audit.

Acceptance Criteria
Drill Mode Scoped Session Start/Stop
Given the Ops Console has an active training sandbox order and Drill Mode is toggled on When a staff member performs interactions in the console and SMS events occur Then a new session is created and tied to the sandbox order with a unique session ID And no interactions are captured when Drill Mode is off And capture stops immediately when Drill Mode is toggled off or the sandbox order is closed And no events from production orders are persisted in the session
Complete Event Capture and Domain Mapping
Given Drill Mode is active for a sandbox order When staff interact with the UI and related backend/SMS events occur Then the session records: clicks, element focus/blur, keystroke latency buckets only (no characters), state changes, API call metadata (endpoint, method, status, duration, payload size), SMS event metadata (direction, template/reference ID), and millisecond timestamps with monotonic ordering And low-level events are mapped to domain actions (check-in received, bay assigned, handoff confirmed, SMS sent) And each domain action in scope has at least one mapped low-level event with timestamp
PII Redaction and Keystroke Privacy
Given any SMS content or notes may include PII When events are captured and persisted Then raw keystrokes are never stored; only latency buckets are recorded And PII in SMS/notes is redacted per policy before persistence (e.g., phone masked except last 4, names masked, license plates hashed) And no unredacted PII appears in stored session payloads or logs And automated checks block persistence if redaction fails
Low-End Mobile Performance Overhead
Given a low-end mobile device running the Ops Console with Drill Mode enabled When a 15-minute drill session is executed Then median interaction latency overhead is ≤ 5% versus Drill Mode off And CPU utilization overhead is ≤ 5 percentage points And memory overhead is ≤ 30 MB And initial page load time delta is ≤ 5% And no more than 5% additional dropped frames versus baseline
Replayable Compact Session Artifact
Given a completed drill session When a coach opens the session for replay Then playback reconstructs the action timeline and UI states deterministically in recorded order And domain actions render with timestamps aligned within ±100 ms of recorded times And the session object size is ≤ 500 KB per 15 minutes of activity And replay initializes within 2 seconds on broadband and 5 seconds on 3G-equivalent networks
Retention Policy and Secure Share Links
Given an organization-level retention policy (e.g., 30 days) is configured When the retention period elapses Then sessions are auto-deleted irreversibly and excluded from search/export And shareable session links use unguessable IDs with ≥ 128 bits of entropy And access to a session via link requires Coach or Admin role and is logged with user, timestamp, and IP And share links honor expiration and can be revoked immediately
Instant Replay Viewer
"As a shift lead, I want an instant, tappable replay with a labeled timeline so that I can review and coach the team moments after a drill while the context is fresh."
Description

Provide an instant, tappable replay immediately after a drill completes, with a scrub-able timeline of domain actions and UI interactions. Support play/pause, 0.5x–2x speeds, step-by-step advance, and jump-to-error markers. Overlay contextual metadata (order, bay, elapsed time) and allow annotation for coaching. Ensure the viewer is responsive for back-office desktops and on-the-lot mobile devices, with offline prefetch for low-connectivity sites. Include secure share links scoped to the drill and role-based access within CurbPing.

Acceptance Criteria
Immediate Replay After Drill Completion
Given a drill run completes successfully, When the completion event is emitted client-side, Then a Replay CTA becomes visible and tappable within 2 seconds on both desktop and mobile success views. Given the user taps/clicks the Replay CTA, When the viewer opens, Then the first frame renders within 1 second if prefetched or within 3 seconds from network and playback is paused by default. Given the replay viewer is open, When the user navigates away and returns within the same session, Then the replay resumes at the last position within 200ms.
Timeline Scrubbing, Playback Controls, and Speed Adjustment 0.5x–2x
Given a replay is open, When the user drags the timeline via mouse or touch, Then the current frame updates with an average seek latency under 150ms and releases to the exact action boundary. Given a replay is open, When the user presses Step Forward/Back, Then playback advances or rewinds by exactly one domain action and updates timecode accordingly. Given a replay is open, When the user toggles Play/Pause, Then the state changes within 100ms and the control reflects the current state. Given a replay is open, When the user selects a playback speed of 0.5x, 1x, 1.5x, or 2x, Then playback runs at the chosen speed with drift < 100ms over 2 minutes and the selected speed persists for the user across sessions.
Jump-to-Error Markers
Given the underlying drill contains N error events, When the replay loads, Then exactly N markers render on the timeline at their timestamps with accessible labels describing the error types. Given error markers are present, When the user activates a marker (click/tap/keyboard), Then playback seeks to max(0, markerTime - 1s), highlights the related action, and pauses. Given multiple error markers exist, When the user uses Next/Previous Error controls, Then focus cycles through markers in chronological order and wraps at ends. Given a drill has no errors, When the replay loads, Then error controls are hidden or disabled and no markers render.
Contextual Metadata Overlay (Order, Bay, Elapsed)
Given a replay is open, When playback progresses across actions, Then the overlay continuously displays Order ID, Bay number (or Unassigned), and Elapsed time and updates within 200ms of the current action. Given overlay visibility is toggleable, When the user toggles Metadata, Then the overlay hides/shows without affecting playback position. Given a drill’s recorded metadata, When compared to overlay values, Then values match the source log exactly for Order ID and Bay and elapsed time accuracy is within ±100ms. Given accessibility standards, When inspected, Then overlay text meets WCAG AA contrast and is screen-reader announced with proper labels.
Annotation and Commenting for Coaching
Given a user with Coach or Manager role, When they add an annotation, Then it is time-anchored to the current replay position, appears on the timeline, and is visible to all users with access to the drill. Given an annotation author or a Manager, When they edit or delete their annotation, Then changes persist and the timeline updates within 1 second. Given network disruption during annotation, When the user saves, Then the annotation queues locally with a Pending state and syncs within 10 seconds after reconnect, preserving the original timestamp. Given a Viewer role without annotate permission, When they open the replay, Then annotation controls are not available and existing annotations are read-only.
Responsive Viewer with Offline Prefetch
Given devices with screen widths from 360px to 1920px, When the replay opens, Then all controls are visible without overlap, primary touch targets are ≥44x44dp, and timeline gestures work in both portrait and landscape. Given the device rotates, When orientation changes, Then playback position, speed, and overlay visibility are preserved. Given the device is online at drill completion, When prefetch starts, Then all assets and the action log for the drill cache within 10 seconds and within a 5MB budget. Given prefetch completed, When the device goes offline, Then the replay loads and plays end-to-end without network calls; if prefetch did not complete, Then the viewer presents an Offline: missing data message and disables Play.
Secure Share Links and Role-Based Access
Given a user with Manager or Coach role, When they generate a share link, Then the link is scoped to the specific drill ID, is view-only by default, and includes a signed token with an expiry between 1 and 30 days. Given a valid share link, When an unauthenticated user opens it, Then the replay viewer renders with only view permissions; when the token is invalid or expired, Then access is denied with HTTP 403 and no drill data leaks. Given an issued share link, When the creator revokes it, Then subsequent accesses are denied within 60 seconds and the event is recorded in the audit log with user, drill ID, timestamp, and action. Given organization role rules, When an authenticated user with insufficient permissions follows a share link, Then annotate and share controls are hidden and privileged data is not accessible; when a user has higher permissions, Then their native permissions apply but scope remains restricted to the same drill.
Click‑Slow Heatmaps
"As a trainer, I want click-slow heatmaps over the UI so that I can see which elements cause hesitation and prioritize fixes."
Description

Generate heatmaps that visualize hesitation time and interaction density across the Ops Console during a drill, highlighting elements with high dwell or repeated mis-clicks. Support per-step overlays aligned to the replay timeline and aggregate views across multiple drills to identify systemic UX friction. Exclude sensitive text content, and provide filters by location, device type, and workflow. Export summary insights for design and training backlogs.

Acceptance Criteria
Per-Drill Heatmap Generation
Given a completed drill with recorded interactions When a user opens Mistake Replay for that drill and selects the Heatmap view Then a heatmap overlay renders on the Ops Console within 2 seconds And each interactive element displays dwell_ms and click_count metrics sourced from that drill’s event log And the heatmap legend shows the color scale ranges for dwell (ms) and click density And the time window displayed matches the drill start–end timestamps
High Dwell and Mis-Click Highlighting
Given heatmap metrics are available for a drill When dwell_ms for an element exceeds 800 ms OR mis_click_rate is >= 15% OR repeat_clicks_on_same_element within 2 seconds is >= 3 Then that element is visually highlighted with a red outline and increased heat intensity proportional to severity And a tooltip on hover/tap shows element_id, label, avg_dwell_ms, median_dwell_ms, click_count, mis_click_count, mis_click_rate, and repeat_click_sequences And a legend explains the thresholds used for highlighting
Step Overlay Synced to Replay Timeline
Given a drill with N labeled steps and a replay timeline When the user scrubs to any timestamp t or uses Next/Previous Step Then the heatmap updates to show only interactions from the active step within 200 ms of the replay position And the step label and time range are displayed above the heatmap And playing the replay auto-advances the heatmap step overlays in sync with the timeline
Aggregate Multi-Drill Heatmap View
Given the user selects Aggregate mode and a date range When at least 5 drills that match the selection exist Then the system renders an aggregate heatmap computed across the selected drills And per-element metrics include avg_dwell_ms, median_dwell_ms, click_rate_per_session, mis_click_rate, and sample_size_sessions And a Top Friction list shows the 10 elements with highest combined friction score = zscore(dwell)+zscore(mis_click_rate) And if fewer than 5 drills match, the view displays “Insufficient data” and disables export
Sensitive Content Exclusion and Redaction
Given UI regions may contain sensitive text (e.g., customer names, phone numbers, order notes) When the heatmap is displayed or exported Then no raw sensitive text is rendered or included in the DOM, tooltips, screenshots, or exports And any on-screen text nodes within heatmap layers are masked as •••• And inspecting the DOM or copying the canvas reveals no sensitive strings matching common PII patterns (e.g., phone, email) And exported files contain only element identifiers and metrics, not UI text content
Filtering by Location, Device Type, and Workflow
Given drills span multiple locations, device types, and workflows When the user applies any combination of filters (Location multi-select, Device Type: Desktop/Tablet/Mobile, Workflow multi-select) Then the heatmap, metrics, and counts update within 2 seconds to reflect only matching sessions And active filters are displayed as removable chips And clearing all filters restores the default All selection And the filter state persists during the current session until changed
Export Summary Insights for Backlogs
Given a single-drill or aggregate heatmap with optional filters applied When the user clicks Export > Summary Insights Then CSV and JSON files are generated within 5 seconds and begin downloading And each record includes: element_id, element_label, avg_dwell_ms, median_dwell_ms, click_count, mis_click_count, mis_click_rate, sample_size_sessions, date_range, filters_applied, generated_at (UTC) And filenames follow heatmap_summary_{mode}_{yyyy-mm-ddThhmmssZ}.(csv|json) And exports exclude any sensitive text content and reflect the currently applied filters
Ideal Path Benchmarking
"As an operations director, I want each workflow compared to an ideal path with target timings so that we can standardize best practices across locations."
Description

Define and version an ideal path per workflow (e.g., arrival-to-bay assignment-to-handoff) with required steps, acceptable variants, and target timings. During replay, compare the recorded session to the ideal path, flagging deviations, skipped steps, and time overruns. Support multi-location templates with local overrides and publish updates without code deploys. Surface a deviation summary at the end of each drill and feed results into coaching reports.

Acceptance Criteria
Versioned Ideal Path Creation and Activation
Given I am an authorized admin When I create a new ideal path draft for a workflow with named steps (required/optional), acceptable variants, and target timings Then the draft is saved with a unique version ID, author, timestamp, and changelog note And publish is disabled until validation passes Given a valid draft When I publish it Then it becomes the active version for the selected workflows/locations and the previous active version is retained in history And an audit log entry records publisher, version ID, scope, and timestamp Given an active version exists When a new drill starts Then the drill stores the active version ID at start time and uses it for all comparisons Given in-flight drills When a new version is published Then in-flight drills continue with their locked version and new drills use the newly active version Given a previous version in history When I set it to active Then it becomes the active version without code deploy and history remains intact
Definition Constraints for Steps, Variants, and Timing Targets
Given an ideal path draft When validation runs Then every required step must have a unique step ID, display name, order index, targetDurationMs > 0, and maxDurationMs >= targetDurationMs And optional steps must have unique IDs and display names if present And every acceptable variant must map to an existing required step and may override targetDurationMs and maxDurationMs only if maxDurationMs of the variant <= maxDurationMs of the parent step And any step or variant missing required fields fails validation with field-specific error messages Given duplicate step IDs or non-sequential order indices When validation runs Then publish is blocked and the errors enumerate the conflicting IDs/indices Given a draft with zero required steps When validation runs Then publish is blocked with an error indicating at least one required step is needed
Replay Comparison Flags Deviations, Skips, and Overruns
Given a completed drill session with an event timeline and a stored ideal path version ID When the replay runs the comparison Then each required step is marked as one of: matched, matched-as-variant, skipped, out-of-order, or time-overrun And any event not mapped to a required step or declared optional step is marked as extra-step And time-overrun is flagged when actualDurationMs > maxDurationMs for that step And the comparison produces counts of skipped, extra, out-of-order, and overrun steps, plus total actual time vs total ideal target time and deltaMs And the replay timeline and heatmap display visual markers for each flagged deviation at the correct timestamps And the comparison uses the version locked to the drill at start time
Multi-Location Templates with Local Overrides
Given a global ideal path template assigned to multiple locations When a location inherits the template Then the effective path equals global template values by default Given a location editor with permissions When they add local overrides for target timings or acceptable variants Then those overrides apply only to that location and are stored with attribution and timestamp And required steps from the global template cannot be removed or reordered by local overrides And attempts to remove or relax a required constraint are blocked with a clear error Given the global template is updated and published When locations have no conflicting overrides Then the updates propagate to those locations And when locations have overrides for the updated fields Then local overrides take precedence and a change log shows which fields remained overridden
Zero-Deploy Publish and Propagation
Given a validated draft ideal path version When an admin clicks Publish in the web console Then no code deploy or service restart is required to activate it And the new active version is available to new drills across all impacted locations within 60 seconds of publish time And in-flight drills are unaffected and continue using their locked version And an audit entry records propagation start and completion timestamps
End-of-Drill Deviation Summary Panel
Given a drill replay completes When the summary view is shown Then it displays: ideal path version ID, total steps, counts of skipped/extra/out-of-order/overrun, total ideal time, total actual time, and deltaMs And it lists each step with status, actual vs target timing, and any variant used And it is available within 3 seconds of replay completion and loads within 1 second on a typical broadband connection And it provides a one-tap Do Over action that starts a new drill for the same workflow and location using the current active ideal path version And the summary is responsive and accessible (keyboard navigable and screen-reader labeled for all metrics)
Coaching Reports Consume Deviation Results
Given a drill finishes and its deviation summary is generated When reporting jobs run Then the drill’s metrics (user, role, location, workflow, ideal version ID, counts per deviation type, total deltaMs, step-level statuses) are persisted to the coaching datastore And aggregated coaching reports (per user, per location, per workflow, and time range) reflect the new data within 5 minutes of drill completion And reports include trend lines for average deltaMs and deviation rate and allow CSV export with the same fields And data retention and access honor role-based permissions so users only see locations they are authorized for
Impact Projection Analytics
"As a GM, I want projected changes to dwell time, reroutes, and tip conversion displayed for each deviation so that I can quantify the value of improvements before rolling them out."
Description

Compute and display projected impact deltas for each deviation and proposed fix, including median dwell time reduction, reroute avoidance likelihood, and tip conversion lift. Use historical CurbPing data models per location and time-of-day, showing confidence ranges and assumptions. Present a concise impact panel in the replay and a drill summary, with export to CSV and webhook to BI tools. Ensure models exclude PII and are recalibrated nightly.

Acceptance Criteria
Replay Impact Panel: Per-Deviation and Fix Deltas
Given a completed Mistake Replay containing at least one deviation and one proposed fix When the replay view loads Then an Impact Panel renders within 1.5 seconds p95 and displays, for each deviation and proposed fix pair: median dwell time delta (seconds), reroute avoidance likelihood delta (%), and tip conversion lift (percentage points), each with sign and units And the panel labels the active segment as Location=<location_name>, Time Bucket=<hourly_local_bucket> And the panel shows model_version and recalibrated_at timestamps And if a metric cannot be estimated due to insufficient sample (n<500), the metric is replaced with “Insufficient data” and a tooltip explains the threshold And no personally identifiable information is present anywhere in the panel
Segmented Models by Location and Time-of-Day
Given historical data exists for multiple locations and hours of day When projections are requested for Location A at 11:00–11:59 local and Location A at 19:00–19:59 local Then the model for each segment is trained/evaluated on its segment-specific data window and returns potentially different deltas And the UI and exports identify the segment_id composed of location_id and hourly_local_bucket And if a segment has <500 qualifying events in the last 30 days, the system falls back to a defined backup segment (adjacent hour or sitewide model) and marks fallback=true in outputs And all timestamps are interpreted in the location’s local timezone
Confidence Ranges and Assumptions Disclosure
Given an Impact Panel is displayed When a user expands Confidence & Assumptions Then each metric shows a 90% confidence interval (low, high) alongside the point estimate and the sample_size used And assumptions list includes: training_window_days, minimum_sample_threshold, reroute_definition, tip_conversion_definition, and any applied fallback segment And confidence intervals are monotonic (low <= estimate <= high) and computed with a documented method stored as ci_method in outputs
Drill Summary CSV Export
Given a drill summary with computed impact is open When the user clicks Export CSV Then a CSV downloads within 2 seconds p95 for up to 10,000 rows, named curbping_impact_<drill_id>_<UTC_timestamp>.csv And the CSV headers include exactly: drill_id, location_id, hourly_local_bucket, deviation_id, fix_id, dwell_time_median_delta_s, dwell_time_ci90_low_s, dwell_time_ci90_high_s, reroute_avoid_lift_pct, reroute_ci90_low_pct, reroute_ci90_high_pct, tip_conv_lift_pp, tip_ci90_low_pp, tip_ci90_high_pp, sample_size, model_version, recalibrated_at, fallback, assumptions_summary And all rows populate types correctly (numbers as numeric, timestamps ISO-8601) And the file contains no PII fields (e.g., phone, name, license_plate, message_body)
BI Webhook Delivery
Given a BI webhook endpoint and shared secret are configured When a drill is completed or a Mistake Replay impact is finalized Then the system POSTs a JSON payload within 10 seconds containing: event_type, drill_id, location_id, hourly_local_bucket, per_item metrics (as in CSV), sample_size, model_version, recalibrated_at, fallback, assumptions, and a created_at timestamp And the request includes an HMAC-SHA256 signature header over the body using the shared secret And 2xx responses are treated as success; 4xx are dropped without retry; 5xx/timeouts retry up to 5 times with exponential backoff (max 10 minutes), then route to a dead-letter queue with reason And payloads contain no PII And delivery successes and failures are logged with correlation_id
Nightly Model Recalibration and Fallback
Given the system clock reaches 03:00 local time for each location When the nightly recalibration job runs Then models are retrained per location and hourly_local_bucket using the last 30 days of eligible events And each successful train increments model_version and records recalibrated_at, training_window_days, and validation metrics (e.g., MAE, AUC where applicable) And if training fails or validation underperforms baseline by >5%, the previous model is retained and an alert is sent to operations; outputs set training_status=failed and model_version remains unchanged And inference immediately uses the latest successful model without service interruption
PII Exclusion in Models, UI, and Telemetry
Given access to training features, rendered UI, CSV exports, webhook payloads, and application logs When automated scanners check for PII patterns (phone numbers, names, emails, license plates, free-text messages) Then no PII features are present in model training/serving feature sets And no PII appears in UI, CSV, webhook payloads, or logs for this feature And attempts to include PII in custom fields are rejected with a validation error and are not persisted in outputs
One‑Tap Do Over
"As a team member, I want a one-tap Do Over of the same scenario or the stumbling step so that I can cement the fix through immediate repetition."
Description

Offer a one‑tap Do Over from the replay screen that instantly replays the same scenario in the training sandbox or creates a focused micro‑drill starting at the stumbling step. Preload the necessary mock order, messages, and bay context; auto-start timing; and re-measure against the ideal path. Provide spaced-repetition suggestions if the user repeats the same error. Keep all Do Overs isolated from live operations and SMS customers.

Acceptance Criteria
One-Tap Do Over: Full Scenario Replay in Sandbox
Given a user is viewing a completed drill replay with a visible Do Over button When the user taps Do Over and selects Full Scenario Then the training sandbox launches within 3 seconds And the same mock order ID, customer message thread, assigned bay, and staff role context are preloaded And a Training — Sandbox banner is displayed on all screens And the timer auto-starts within 300 ms of the scene rendering And no outbound SMS or API calls to production gateways are made And all actions are captured in the training audit log
One-Tap Do Over: Micro-Drill From Stumbling Step
Given the replay timeline has a flagged stumbling step When the user taps Do Over at that step or selects Micro-Drill Then the micro-drill starts at the flagged step with all prior prerequisites simulated And only the steps from the stumbling step to completion are required And irrelevant UI elements not needed for the micro-drill are hidden or disabled And the timer auto-starts within 300 ms of the micro-drill scene rendering And completion is recorded when the final required step is done
Auto-Preload Context and Timing Behavior
Given a Do Over (full or micro-drill) is launching When preloading occurs Then mock order details, customer messages, assigned bay, and staff permissions are auto-populated before the scene becomes interactive And the timer auto-starts without any additional user action And the user can cancel within 2 seconds to stop timing and exit back to replay And a preload failure after 5 seconds triggers a retry up to 2 times and logs an error event
Re-Measurement Against Ideal Path and Results Display
Given a Do Over is completed When results are computed Then the system displays per-step time, total completion time, number of reroutes, and a click heatmap And the deltas versus the ideal path are shown as absolute and percentage differences And an improvement or regression label is presented based on delta thresholds And results are saved to the trainee profile and linked to the source replay for comparison
Spaced-Repetition Suggestions After Repeated Error
Given the same error type recurs 2 or more times within 7 days for the same user across drills or Do Overs When the latest Do Over completes Then the system presents suggestions with options: Repeat 3 times now, Remind me in 2 days, Add to practice set And selecting an option schedules the next practice and adds it to the user’s training queue And dismiss or snooze actions are logged and no customer-facing notifications are sent And future reminders are delivered only via training channels
Isolation From Live Operations and Customer Communications
Given any Do Over (full or micro-drill) is running When network requests are made Then only sandbox or test endpoints are used and no production state is mutated And no SMS, calls, or push messages are delivered to customers And no production bay inventory or live order assignments are consumed And training data is stored under a training namespace and excluded from live dashboards and exports by default And a training watermark appears on all mock communications and screens

Confidence Meter

Real‑time proficiency scoring across speed, accuracy, and guest empathy, rolling up to a simple ‘Shift‑Ready’ badge. Auto‑shares a short onboarding report with managers and flags modules needing another pass. Gamified micro‑goals keep learners engaged and standards consistent.

Requirements

Real-time Performance Telemetry
"As a shift lead, I want real-time metrics on each interaction so that I can see where my team is excelling or needs help during the shift."
Description

Implement lightweight client- and server-side event capture to measure speed (arrival-to-handoff time, response latency), accuracy (order match rate, remake rate), and guest empathy (SMS sentiment, CSAT quick-reply) per interaction. Stitch events across CurbPing’s order timeline (order ready, bay assignment, 'I’m here', handoff) without requiring apps or hardware. Ensure sub-100ms ingestion overhead, idempotent event processing, and graceful handling of missing signals. Store normalized, privacy-safe metrics keyed to staff IDs and shift context for downstream scoring and coaching.

Acceptance Criteria
Sub-100ms Telemetry Ingestion Overhead
Given telemetry events are emitted during order interactions under 100 RPS sustained and 200 RPS peak, When events traverse the ingestion pipeline (edge collector -> queue -> normalizer -> store), Then the added end-to-end latency to the user-critical path is p95 <= 100ms and p99 <= 150ms as measured by server-side timings. Given a client generates an event in the browser "I'm here" flow, When the event is sent asynchronously, Then main-thread blocking time is <= 10ms and payload size <= 2KB for standard events. Given a regional failover is initiated, When ingestion flips to secondary infrastructure, Then p95 added latency remains <= 150ms and core event drop rate remains <= 0.01% (p99.9 success).
Idempotent Event Processing and De-Duplication
Given two or more events share the same idempotency_key within a 36-hour window, When they arrive concurrently or out of order, Then exactly one event is persisted and counted; subsequent duplicates return HTTP 200 with dedup=true and do not alter metrics. Given 1,000 duplicate submissions of the same event, When processed, Then resulting metric increments equal 1 and the deduplication rate equals 100%. Given client retries due to network timeouts, When the same idempotency_key is reused, Then the server responds with the original correlation_id and no side effects occur.
Event Stitching Across Order Timeline
Given events order_ready, bay_assigned, im_here, and handoff share the same order_id, When received within a 2-hour session window, possibly out of order and with up to ±5s clock skew, Then the system links them into a single interaction and calculates arrival_to_handoff_seconds and first_response_latency_ms correctly. Given bay_assigned is missing but im_here and handoff are present, When stitching the interaction, Then metrics not requiring bay_assigned are computed and the record is flagged completeness=false. Given multiple handoff events exist for the same order_id, When stitching the timeline, Then only the first handoff in the window is used for speed metrics and subsequent handoffs are recorded as remakes.
Graceful Handling of Missing or Late Signals
Given an interaction where im_here never arrives, When the shift ends or 2 hours elapse, Then the interaction closes with status=partial, speed KPIs depending on im_here are null, and no errors propagate to the client. Given a late event arrives after the session window, When processed, Then it is stored as an orphaned event with references but excluded from KPI calculations and a diagnostic is logged. Given queue backpressure under 10x normal load for 5 minutes, When processing events, Then non-critical diagnostics are shed first, core event success remains >= 99.9%, and API responses stay 2xx without blocking user flows.
Privacy-Safe Metrics Storage Keyed to Staff and Shift
Given a metric record is persisted, When written to storage, Then it is keyed by staff_id and shift_id (with location_id and role) and excludes customer PII (no phone numbers; no raw message text beyond derived sentiment score). Given data at rest and in transit, When inspected, Then records are encrypted at rest (AES-256) and transmitted over TLS 1.2+. Given an access request from a non-manager role, When querying metrics, Then access is denied; manager role views only aggregated metrics with row-level filtering by location. Given a customer data deletion request, When executed, Then raw SMS content older than 30 days is purged/redacted and aggregated metrics remain as non-PII summaries.
Speed & Accuracy Metrics Capture and Calculation
Given complete timeline events exist for an order, When processed by the telemetry pipeline, Then arrival_to_handoff_seconds and first_response_latency_ms are computed per interaction and exposed via the metrics API and data store. Given a labeled fixture dataset of 100 interactions, When processed end-to-end, Then computed arrival_to_handoff_seconds match expected values within ±1 second for 100% of fixtures. Given handoff events linked to orders and remakes flagged, When aggregating accuracy metrics, Then order_match_rate = matched_handoffs / total_handoffs and remake_rate = remakes / total_orders are computed per day and per shift and match fixture expectations. Given the browser-based "I'm here" flow via SMS link and server-side signals only, When running an end-to-end test without any native app or hardware beacons, Then all required speed and accuracy metrics are captured successfully.
Guest Empathy Metrics via SMS Sentiment and CSAT
Given inbound customer SMS replies during or after pickup, When processed by the classifier, Then sentiment is categorized (positive/neutral/negative) with >= 90% macro-precision on a held-out test set and a normalized empathy_score in [-1, 1] is stored per interaction. Given CSAT quick replies (1–5 digits, common emojis, or short phrases), When parsed, Then they are normalized to csat_score 1..5; invalid or opt-out messages (e.g., STOP) are excluded and do not affect scores. Given staff_id and shift_id context, When aggregating empathy metrics, Then per-staff and per-shift aggregates are produced with freshness <= 5 minutes and exposed via the scoring feed.
Configurable Scoring Engine
"As an operator, I want scoring that reflects my store’s standards so that the badge and recommendations align with how we run shifts."
Description

Build a rules-driven engine that computes proficiency scores across speed, accuracy, and empathy with configurable weights per location and role. Support time-decay, minimum sample thresholds, and outlier resistance. Generate per-interaction, per-shift, and rolling period scores, and output a single composite “Shift-Ready” score. Version and audit scoring configurations to allow safe iteration and A/B testing without disrupting operations.

Acceptance Criteria
Per-Location and Role Configurable Weights
Given a location and role have a weight config for speed, accuracy, and empathy that sums to 1.0 When the engine computes a composite score for an interaction or aggregate period Then the composite equals the weighted sum of the three dimension scores within ±0.1% tolerance Given a role-specific weight config is missing for a location When computing a score Then the engine falls back to the location default, and if absent, to the global default, and records a fallback event in the audit log Given a submitted weight config contains negatives or does not sum to 1.0 within ±0.1% When attempting to save the config Then validation fails with a descriptive error and no new version is created
Time-Decay Weighting for Rolling Period Scores
Given a decay half-life H (in days) is configured per dimension and a 30-day rolling window When computing a rolling period score Then each interaction with age t days is weighted by w = exp(-ln(2) * t / H), and interactions older than the configured lookback limit are excluded Given decay is set to 0 (disabled) for a dimension When computing its rolling score Then all interactions within the window have equal weight Given a sample set of dated interactions and H=10 days When computing the rolling score Then the produced score matches a reference implementation within ±0.5 absolute points on a 0–100 scale
Minimum Sample Threshold Enforcement
Given a minimum sample threshold T is configured per dimension and aggregate type When the count of qualifying interactions for an aggregate period is fewer than T Then the dimension score is not emitted and is marked status="insufficient_data" with reason="below_threshold" Given any dimension is below threshold for a composite aggregate When computing the composite Then the composite is not emitted and the aggregate is marked status="insufficient_data" with the impacted dimensions listed Given per-interaction scoring is requested When producing a per-interaction result Then thresholds do not suppress per-interaction dimension scores
Outlier Resistance in Aggregation
Given an outlier policy of winsorization at the 5th and 95th percentiles is configured for speed metrics When aggregating a period score Then values below P5 are set to P5 and values above P95 are set to P95, and the count of adjusted points is recorded Given a dataset where a single interaction is a 10x outlier relative to the interquartile range When computing the aggregate score with the configured outlier policy Then the aggregate changes by less than 5% compared to the aggregate with the outlier removed Given outlier detection flags any interaction When persisting the aggregate Then the audit record includes the number of outliers and the applied policy identifier
Per-Interaction, Per-Shift, and Rolling Outputs with Timezone Fidelity
Given interactions have timestamps and the location has a stored timezone and shift schedule When computing per-shift aggregates Then interactions are bucketed by completion timestamp into shifts in the location's timezone, including daylight saving transitions Given a shift ends When computing the per-shift aggregates Then dimension and composite scores are available within 60 seconds of shift end via API and UI Given rolling windows of 7 and 30 days are requested When computing aggregates Then the engine returns per-dimension and composite scores on a 0–100 scale with two decimal precision
Versioning, Auditability, and Safe A/B Testing of Scoring Configs
Given scoring configurations are versioned with immutable version IDs and effective_at timestamps When a new configuration is published Then the previous versions remain retrievable and no in-flight computations are retroactively changed Given two active configuration versions are assigned to A/B cohorts via stable hashing on employee_id When scores are computed during a shift Then each employee remains in the same cohort for the entire shift and each score record is stamped with config_version_id and cohort_id Given a historical score is requested When the engine retrieves the record Then it returns the score computed with its original config_version_id and includes an audit trail showing who published that version and when
Shift-Ready Badge & Coaching UI
"As a crew member, I want a simple badge with clear tips so that I know exactly what to improve before and during my shift."
Description

Surface an at-a-glance “Shift-Ready” badge (Ready, Needs Focus, Not Ready) on the staff dashboard with hover/tap details on contributing metrics. Provide inline, actionable coaching tips tailored to the lowest sub-score (e.g., reduce response latency by enabling quick-replies). Ensure mobile-friendly, low-clutter UI that fits within existing CurbPing workflows and respects SMS-first usage.

Acceptance Criteria
Shift-Ready Badge Visible on Staff Dashboard
Given a logged-in staff member is on the Staff Dashboard When the dashboard completes its initial data fetch Then a Shift-Ready badge is displayed as one of "Ready", "Needs Focus", or "Not Ready" And Given composite proficiency score S (0–100) When determining the badge state Then map S ≥ 80 to Ready, 60 ≤ S < 80 to Needs Focus, and S < 60 to Not Ready And When the dashboard loads Then the badge is visible within 1.0 second after initial content is rendered And Then the badge includes an accessible label in the format "Shift-Ready: {Status} ({S}%)"
Badge Detail via Hover/Tap
Given a desktop pointer device When the user hovers the Shift-Ready badge for at least 100 ms Then a detail panel appears within 200 ms showing Speed, Accuracy, and Empathy sub-scores (0–100) and a "Last updated" timestamp And Given a touch device When the user taps the badge Then a bottom sheet opens with the same sub-score details and timestamp And When the pointer leaves the badge area or the user taps outside the sheet/panel Then the detail UI closes without navigating away
Inline Actionable Coaching Tip by Lowest Sub-score
Given sub-scores are available for Speed, Accuracy, and Empathy When the detail panel/sheet is opened Then exactly one inline coaching tip is shown targeting the lowest sub-score And Given a tie on the lowest sub-score When selecting the targeted area Then apply deterministic priority: Accuracy > Speed > Empathy And Then the tip includes a specific action and an enabled CTA (button or link) that routes to the relevant setting or training module (e.g., Speed → Enable Quick Replies) And When the lowest sub-score changes Then the displayed coaching tip updates within 2 seconds
Mobile-Friendly Low-Clutter Presentation
Given a viewport width between 320 px and 375 px When viewing the Staff Dashboard Then the Shift-Ready badge and a one-line status summary are visible above the first scroll boundary with no horizontal scrolling And Then all interactive elements related to the badge have touch targets ≥ 44 × 44 px and text/icon contrast ratio ≥ 4.5:1 And Then cumulative layout shift attributable to the badge and its details after first render is < 0.1
Seamless Workflow Integration
Given staff are managing live orders on the dashboard When the badge and its details are used Then order list interactions remain available and unobscured And When opening or closing the detail UI Then no full-page navigation occurs and the order list scroll position is preserved And Then the badge resides in the dashboard header area and requires zero additional navigation steps to access
Real-Time Updates Without Reload
Given an underlying metric update occurs (e.g., a new response-latency sample) When the dashboard is connected Then the badge state and sub-scores update within 10 seconds without a full page reload And Then an "Updated <n>s ago" indicator reflects the most recent refresh within ±1 second accuracy And Then score and badge transitions animate ≤ 150 ms and do not introduce layout jumps
SMS-First, No App/Hardware Requirement
Given the dashboard is accessed via a standard mobile or desktop browser When using the Shift-Ready badge and coaching features Then no native app installation or external hardware is required And Then all CTAs resolve to web destinations that open in the default browser And When loading assets unique to this feature for the first time Then the additional compressed payload is ≤ 150 KB over the wire
Auto Onboarding Report Delivery
"As a manager, I want an automatic summary of new hires’ progress so that I can target coaching without digging through data."
Description

Automatically compile a concise onboarding report after predefined milestones (e.g., first 3 shifts or 50 orders) highlighting strengths, gaps, and recommended modules. Deliver to managers via SMS link and email, with web access in the manager console. Include change logs and next steps while minimizing manager effort; ensure delivery windows avoid rush periods.

Acceptance Criteria
Milestone-Triggered Report Compilation
Given a new hire completes a configured onboarding milestone (first 3 shifts OR 50 orders) at a location with Confidence Meter enabled And the employee, location, and manager records are valid When the milestone completion event is recorded Then the system compiles an onboarding report within 5 minutes of the event And the report time range covers from hire start to milestone completion time And the report includes strengths, gaps, and recommended modules derived from speed, accuracy, and empathy metrics And the report includes a change log since the prior report and clearly labeled next steps
Rush-Period Delivery Deferral
Given rush windows are configured per location or default to 11:30–14:00 and 17:00–20:00 local time When a report becomes Ready during a rush window Then SMS and email notifications are deferred until the next non-rush window And delivery occurs within 10 minutes after the rush window ends And if the report becomes Ready outside rush windows, notifications are sent within 5 minutes And if generated after closing hours, notifications are sent within 30 minutes of opening on the next business day
Multi-Channel Delivery and Read Tracking
Given a manager has a verified email and SMS-capable phone on file When a report is Ready Then an SMS with a secure link and an email with the same link are sent to all assigned managers And each channel records a Delivered timestamp upon provider confirmation And if a channel fails, it is retried up to 3 times with exponential backoff (1, 5, 15 minutes) And a final failure status with reason code is recorded in the audit log
Secure, Low-Friction Access
Given notifications contain a signed, single-report access token valid for 7 days When a manager opens the link within the validity window Then the report loads without additional login and is scoped to the manager’s permitted locations And the link becomes unusable after expiration or revocation And invalid or expired links display a safe error page with a path to request a fresh link And the report summary and next steps are visible above the fold on a mobile device and require no more than 2 taps to view details
Report Content and Format Completeness
Given a report is opened Then it displays: employee name, role, location, reporting period, strengths (top 3), gaps (top 3), recommended modules with rationale and estimated time, modules flagged for another pass, change log entries since the previous report, and actionable next steps And each recommended module has a direct launch link And the page is mobile-first, loads in under 2 seconds on a typical 4G connection, and meets WCAG 2.1 AA color contrast And the summary section fits within one mobile screen with collapsible details
Manager Console Access and Auditability
Given a manager with appropriate role signs into the console When they navigate to Onboarding Reports Then they can access the latest report and prior reports per new hire at their locations And they can view per-channel delivery statuses and timestamps And all report generation and delivery events are logged with actor/system, timestamp, and outcome for at least 90 days
Adaptive Module Flagging
"As a crew member, I want the system to point me to the exact module I should redo so that I can improve the right skill quickly."
Description

Map low sub-scores to specific micro-learning modules and automatically flag modules needing another pass. Trigger nudges via SMS with deep links, track completion, and re-evaluate proficiency on completion. Support per-location content mapping and guardrails to prevent over-notifying during peak hours.

Acceptance Criteria
Auto-Flag on Low Sub-Score Detection
Given a staff member’s Confidence Meter evaluation is saved with at least one sub-score below the configured threshold for their location When the evaluation save completes Then a flag is created for each mapped micro-learning module within 5 seconds And each flag records the sub-score value, mapping rule ID/version, staff ID, and timestamp And each new flag’s status is set to "Pending"
Location-Specific Mapping Overrides
Given global mapping rules exist and Location A has its own overrides And a staff member assigned to Location A saves an evaluation When the system evaluates sub-scores against mappings Then Location A’s mapping rules are applied first And if a sub-score has no Location A rule, the corresponding global rule is used And each flag stores which rule source (Location or Global) and version was applied
SMS Nudge with Secure Deep Link
Given a Pending module flag exists for a staff member with a verified phone number When the notification job runs Then an SMS is sent containing a deep link to the assigned module And the platform records the provider delivery status when available And the deep link opens directly to the module for that staff member and flag And the deep link contains a single-use token that expires after 24 hours And link click events are tracked with staff ID, module ID, and timestamp
Peak-Hour Guardrails and Deferral
Given Location A has peak-hour windows and a max nudge limit configured And a new Pending flag is created during a peak-hour window When the system schedules notifications Then the SMS nudge for that flag is deferred until the peak-hour window ends And multiple flags created during the same peak window are bundled into a single deferred SMS And across any rolling 24-hour period, nudges sent to that staff member do not exceed the configured limit
Completion Tracking and Auto Re-Evaluation
Given a staff member opens the deep link and completes the assigned module When the system receives the module completion event Then the corresponding flag is marked "Completed" with a timestamp And the system re-evaluates the associated sub-score within 2 minutes And if the sub-score meets or exceeds the threshold, the flag is cleared and no further nudges are scheduled And if the sub-score remains below threshold, a follow-up nudge is scheduled according to guardrails
Multi-Module Prioritization and Throttling
Given multiple sub-scores fall below thresholds and map to multiple modules with defined priorities When flags are generated and notifications are scheduled Then flags are ordered by priority (highest severity first) And only one SMS nudge is sent per scheduling cycle for the highest-priority Pending flag And lower-priority flags remain Pending and are nudged in future cycles subject to guardrails And no duplicate flags are created for the same staff-module pair within the configured cooldown window
Unmapped Sub-Score Handling
Given a sub-score is below threshold but has no mapping in either Location or Global rules When the evaluation is saved Then no module flag or SMS nudge is created for that sub-score And a configuration warning is logged with the sub-score name, location ID, and timestamp And an audit entry is created so admins can review unmapped sub-scores
Gamified Micro-goals
"As a crew member, I want engaging micro-goals so that improving my skills feels motivating and trackable."
Description

Introduce small, time-bound goals (e.g., achieve <3m median handoff over next 10 orders, 5 perfect handoffs) tied to the weakest metric, awarding points, streaks, and badges. Provide opt-in participation, private-by-default standings, and anti-gaming checks (sample size thresholds). Integrate progress updates via SMS and the dashboard without interrupting order flow.

Acceptance Criteria
Opt‑In Enrollment for Micro‑Goals
Given a user is not enrolled in micro‑goals by default When the user sends the SMS keyword "JOIN" or enables the dashboard toggle Then the system sets enrollment=true and sends a confirmation within 10 seconds And the enrollment state is persisted and visible on the user’s profile And no user is enrolled without explicit opt‑in When the user sends "STOP" or disables the dashboard toggle Then enrollment=false within 10 seconds, future micro‑goal SMS stop, and no new goals are created for that user
Auto‑Target Weakest Metric
Given a user is enrolled and eligible for a new goal When the system evaluates the last 14 days or trailing 20 eligible orders (whichever reaches ≥10 orders first) Then it selects the weakest metric (Speed, Accuracy, or Empathy) per Confidence Meter definitions and ties are broken by Speed > Accuracy > Empathy And the generated goal explicitly references the targeted metric, threshold, sample size, and time window And no goal is created if eligible sample size <10; the system queues evaluation until eligibility is met
Time‑Bound Goal Windows and Evaluation
Given a goal is assigned with fields {id, metric, targetThreshold, sampleSize, timeWindow, startTime} When orders occur after startTime Then only the next sampleSize eligible orders within timeWindow are counted in chronological order And the goal is evaluated as Success if targetThreshold is met at the point sampleSize is reached within timeWindow And the goal is evaluated as No Result if timeWindow expires before sampleSize is reached (no points awarded) And goals for a single user do not overlap; subsequent goals are queued until the current one is resolved
Points, Streaks, and Badges Awards
Given a goal result of Success When the result is posted Then award 50 points by default (configurable), increment the user’s consecutive‑day goal streak if the prior day also had a Success, and issue the appropriate badge per rules (e.g., 3 Consecutive Successes, First Speed Success) And persist awards with timestamps and make them visible on the dashboard within 30 seconds And the same goal cannot award points/badges more than once Given a goal result of Fail or No Result Then award 0 points and do not break the streak unless a full calendar day passes without a Success
Private‑by‑Default Standings and Opt‑In Leaderboard
Given a user is enrolled in micro‑goals Then their standings and awards are visible only to the user and managers by default; peers cannot view them When the user explicitly opts into the leaderboard Then they appear on the team leaderboard within 2 minutes When the user revokes leaderboard consent Then they are removed within 2 minutes and historical ranks are hidden from peers (managers retain access) And access control is enforced by role; attempts by non‑manager peers to view private standings are denied
Anti‑Gaming Thresholds and Validity Checks
Given a goal window is active Then only eligible orders count: completed curbside handoffs not cancelled or refunded for kitchen/stock errors, one count per unique order id/phone within 5 minutes And minimum sample size is enforced per goal (≥10 orders); skipping orders is not allowed When more than 3 order cancellations or manual deletions occur during the goal window Then mark the goal Invalid and notify the manager within 2 minutes When data edits are detected on included orders after evaluation Then re‑evaluate the goal; if integrity cannot be verified, mark Invalid and roll back any awards
Non‑Interruptive SMS and Dashboard Progress Updates
Given a user is enrolled and has an active goal When sending SMS updates Then send at most 3 messages per goal (start, midpoint, result) and never require a reply to continue And do not send during an active order handling state; defer until idle or 10 minutes max When showing dashboard updates in the order management view Then use non‑blocking toasts only; no modal dialogs or focus‑stealing elements are introduced And updates do not add extra clicks to complete order handoffs
Manager Analytics & Trends
"As an operator, I want clear analytics on proficiency over time so that I can staff and coach effectively to reduce wait times and remakes."
Description

Offer a manager dashboard with team-wide trends, distribution of scores, cohort comparisons (new hires vs. veterans), and time-of-day/bay heatmaps. Enable drill-down to individual summaries, export to CSV, and filters by date range, role, and location. Ensure privacy (no PII in exports), fast loading on mobile, and availability during store hours with graceful degradation on poor networks.

Acceptance Criteria
View Team Trends on Manager Dashboard
Given I am a manager with access to one or more locations and a selected date range When I open the Manager Analytics dashboard on web or mobile Then I see team-wide trends for speed, accuracy, and guest empathy with mean, median, and p90 values per day And the distribution histogram for each metric is rendered with correct bin counts And all aggregates reflect events up to the last 60 seconds (data freshness <= 60s) And totals and counts exactly match the underlying event store for the same filters And all times are shown in the location’s local timezone
Compare Cohorts by Tenure
Given staff profiles include hire dates or tenure flags When I toggle Cohort Comparison and select New Hires (<= 30 days) vs Veterans (> 30 days) Then trend lines and summary stats for each cohort render simultaneously for speed, accuracy, and empathy And each cohort’s member count (n) is visible And switching role, location, or date filters updates both cohorts consistently And the difference in means between cohorts is displayed for each metric And counts and values equal those produced by running the same filters separately for each cohort
Visualize Time-of-Day and Bay Heatmaps
Given analytics data includes arrival timestamps and bay numbers When I view Heatmaps Then I see a Time-of-Day heatmap (30-minute bins) and a Bay heatmap (per bay index) for arrivals and average confidence scores And a legend explains the color scale and units And clicking any cell applies a filter matching that bin and updates all charts And hovering shows exact count and averages for that cell And heatmaps exclude PII and never display vehicle or phone details
Drill-Down to Individual Summary from Trends
Given I have permission to view team members in my locations When I click a data point, bar, or table row for a specific user Then I navigate to that user’s Individual Summary And I see last 14- and 30-day trends for speed, accuracy, empathy, current Shift-Ready badge, flagged modules, and a link to the onboarding report And the page loads in <= 2.0 seconds on a 4G mobile device (P95) And a Back to Trends control returns me to the previous filtered view And users outside my managed locations are not accessible
Export Filtered Analytics to CSV (PII-safe)
Given role, location, and date filters are set When I click Export CSV Then a CSV downloads within 5 seconds containing only: user_id_hashed, role, location_id, date, hour, bay, speed_score, accuracy_score, empathy_score, shift_ready, orders_count And the row count equals the number of records in the current view’s underlying dataset And the file is UTF-8 encoded with RFC 4180-compliant headers and comma delimiters And no PII fields (e.g., name, phone, email) are present
Apply Filters by Date Range, Role, and Location
Given the dashboard defaults to Last 7 Days and my primary location When I change date presets (Today, Last 7, Last 30) or set a custom range up to 180 days, and select one or more roles and locations Then all charts, tables, heatmaps, and exports immediately reflect the combined filters (logical AND) And the selected filters persist in the URL and across page reloads for the session And the timezone displayed matches the primary selected location And invalid or empty results show a clear zero-state with no errors
Mobile Performance and Store-Hours Availability with Graceful Degradation
Given it is within the configured store hours and I access the dashboard on a mid-range mobile device over 4G When the network is normal Then P95 Time to Interactive <= 3.0s, P95 First Contentful Paint <= 1.5s, and total transfer size after cache <= 500KB When the network is degraded (latency > 1000ms) or offline Then a read-only cached view from the last successful sync (<= 24h) is shown with a “Limited connectivity” banner and Retry control And unavailable charts display skeletons/placeholders instead of errors And uptime during store hours is >= 99.5% monthly as measured by synthetic monitoring

Shadow Mode

A safe, ghost layer over the live arrivals board where trainees make parallel decisions that affect nothing. The system compares choices to actual staff actions, highlights gaps, and provides just‑in‑time hints. Graduates can enable light ‘Assist‑On’ cues during their first live shifts.

Requirements

Ghost Board Synchronization
"As a trainee curbside coordinator, I want a real-time shadow version of the arrivals board where I can practice taking actions without affecting live orders so that I can learn workflows safely and build muscle memory."
Description

Provide a real-time, read-only mirror of the live arrivals board that spawns a private “shadow” workspace per trainee. Trainees interact with identical UI controls (assign bay, mark ready, mark delivered, select vehicle/location, preview templated SMS) but all actions are sandboxed, produce no side effects, and never send messages. The shadow board remains within 300 ms of live state, gracefully handles order lifecycle changes (e.g., order completed, reassigned bay), and isolates concurrent trainee sessions. Visual watermarks and color theme distinguish Shadow from Live to prevent confusion. Integrates with the existing arrivals feed and order state machine so the training experience exactly matches operational flows on web and mobile browsers, with no additional apps or hardware.

Acceptance Criteria
Shadow Board mirrors Live within 300 ms
Given the live arrivals board emits an order event (create, update, bay assign/reassign, ready, delivered, vehicle/location update) When a trainee has an active shadow session Then the shadow board reflects the event within 300 ms end-to-end for ≥95% of events during business hours And no more than 1% of events exceed 500 ms And event ordering is preserved relative to the live board
Shadow actions are side‑effect free
Given a trainee performs any shadow UI action (assign bay, mark ready, mark delivered, select vehicle/location, preview templated SMS) When the action is submitted Then no writes occur to production order records or the live state machine And zero outbound SMS or customer notifications are sent And the live arrivals board state remains unchanged And the action is recorded only in the trainee’s shadow session audit log
Per‑trainee private shadow workspace isolation
Given two or more trainees are shadowing the same location concurrently When each trainee takes independent actions Then each trainee sees only their own shadow decisions and notes And no trainee can view, overwrite, or interfere with another trainee’s shadow state And terminating one shadow session does not affect live operations or other shadow sessions And concurrent shadow sessions do not degrade shadow latency beyond the defined SLA
UI parity with clear Shadow vs Live differentiation
Given the current Live board UI controls and flows When the shadow board is opened Then all equivalent controls are present and interactable in Shadow (including modals and shortcuts) And any action that would cause a side effect is safely stubbed/blocked with a non-disruptive notice And a persistent “Shadow Mode” watermark and distinct color theme are visible on all screens and modals with contrast ratio ≥4.5:1 And ARIA announcements indicate Shadow context on page load And parity is verified on both desktop and mobile layouts
Shadow board handles live order lifecycle changes gracefully
Given an order transitions lifecycle (completed, canceled, reassigned bay, merged/split, or driver/vehicle update) When the transition occurs on the live board Then the shadow board updates within the latency SLA without client errors And controls for ineligible actions are disabled/cleared on completed or canceled orders And any in-progress trainee interaction is safely canceled with a brief, non-blocking system hint And the visible order list and counts match the live board after update
Shadow session reconnection and stale state handling
Given a trainee’s network drops or the browser is refreshed When connectivity is restored Then the shadow session resynchronizes to the latest live state within 5 seconds And a “Last synced” timestamp updates to within 2 seconds of current time And no trainee shadow actions are replayed to live And if the session is stale for >30 seconds, a banner prompts the trainee to refresh or continue in read-only until resync
Web and mobile browser parity without additional apps or hardware
Given access via supported browsers When Shadow Mode is used on desktop (Chrome, Safari, Edge, Firefox latest 2 versions) and mobile (iOS Safari 16+, Android Chrome 12+) Then all above acceptance tests pass without requiring any app installs or extra hardware And touch targets meet ≥44×44 px on mobile and keyboard navigation works on desktop And frame rate remains ≥45 FPS during typical board interactions And network usage does not exceed live board by >15% for the same event volume
Decision Capture and Event Logging
"As a training manager, I want every trainee action in Shadow Mode to be captured with context and timestamps so that I can review performance and provide targeted coaching."
Description

Record every trainee interaction in Shadow Mode with precise timestamps, order identifiers, action type and parameters (e.g., chosen bay, vehicle color), pre/post simulated state, and device/session metadata. Persist events to an analytics store linked to the trainee profile and shift context to enable replay, auditing, and longitudinal analysis. Capture reaction time relative to live events (arrival detected, bay assigned, hand-off) and preserve data integrity across network hiccups or page reloads. Mask PII (e.g., phone numbers) to align with privacy policies while retaining operational value for coaching.

Acceptance Criteria
Shadow Mode Decision Event Schema Completeness
Given a trainee in Shadow Mode on an active shift and an order is visible on the arrivals board When the trainee performs any supported action (assign bay, set vehicle color, mark ready, add note, undo) Then one event is recorded with required fields: event_id(UUIDv4), timestamp_utc(ISO8601 ms), order_id, trainee_id, shift_id, action_type(in enum), action_params(JSON), pre_state(JSON), post_state(JSON), device.os, device.os_version, device.browser, device.browser_version, session_id(UUID), page_url, source='shadow_mode' And all required fields are non-null and conform to schema validation And the event passes server-side schema validation and is acknowledged
Reaction Time Captured Relative to Live Events
Given a live event exists for the same order (arrival_detected at T_arr, bay_assigned at T_bay, handoff_recorded at T_hand) When the trainee submits an action concerning that order Then the event includes reaction_time fields: rt_from_arrival_ms, rt_from_bay_ms, rt_from_handoff_ms, each equal to max(0, action.timestamp - T_x) when T_x exists, else null And values are integers in milliseconds and < 3600000 And reaction_time fields are persisted and queryable alongside the event
Persistence, Linkage, and Replay Readiness
Given a valid event is produced When it is sent to the analytics store Then it is durably persisted and queryable by event_id within 2 seconds (P95) And it is retrievable by trainee_id + shift_id + order_id with chronological ordering by timestamp_utc And pre_state -> post_state transitions can be replayed to reconstruct the trainee’s Shadow Mode timeline without gaps And ingestion is idempotent using event_id; client retries do not create duplicates
Resilience to Network Loss and Page Reload
Given the device loses connectivity or the page reloads during a shift When the trainee performs actions offline or reconnects Then events are queued locally up to 15 minutes, preserving original timestamp_utc and order sequence And upon reconnect, queued events are transmitted in order and acknowledged without duplication And a stable session_id persists for the shift, while a new page_session_id is recorded per reload And no more than 0.5% of offline events are dropped (monitored daily)
PII Masking and Privacy Compliance
Given any event payload contains contact data or other PII from the order context When the event is stored or transmitted to analytics Then phone numbers and similar identifiers are irreversibly masked or tokenized (e.g., last2 digits only or salted hash) And no clear-text PII appears in action_params, pre_state, post_state, metadata, logs, or dashboards And masking preserves joinability to operational entities via non-PII keys (order_id, shift_id)
Joinability With Live Staff Actions for Comparison
Given live staff action logs exist for the same shift When trainee events and staff actions are queried by order_id and time window Then at least 99% of trainee events can be matched to a corresponding staff action or absence within a ±60s window And the match includes fields to compute agreement/mismatch metrics (e.g., chosen_bay, handoff timing) And unmatched events are reportable with reasons (no corresponding order, outside window)
Immutability and Audit Trail Integrity
Given an event has been persisted When subsequent corrections are needed Then the original event remains immutable; corrections are appended as new events with correction_of referencing the original event_id And each stored event includes server_received_at and event_hash for tamper evidence And any mutation attempt is rejected and audited
Real-time Action Comparator
"As a training system, I want to compare trainee decisions to actual staff actions in real time so that gaps in accuracy and timing are highlighted for immediate feedback."
Description

Implement a comparator that aligns trainee actions with actual staff actions on the same orders within configurable time tolerances. Define equivalence and scoring rules (correct action type, correct bay, correct order, timeliness) with partial credit for near-misses and penalties for missed or out-of-sequence steps. Calculate and surface metrics like accuracy, latency deltas, and adherence, and highlight mismatches directly in the trainee UI and manager views. Handle out-of-order events, concurrent edits, and late arrivals robustly, ensuring idempotent comparisons even when live states change rapidly.

Acceptance Criteria
Time-Aligned Equivalence Matching
Given Shadow Mode is enabled and comparison timeToleranceMs = T And a trainee records action type X on order O at time tT with bay B And staff records action type X on order O at time tS with bay B When |tT - tS| <= T Then the comparator links the two events as a single match And sets result=Equivalent, score=1.0, latencyDeltaMs = (tT - tS) And updates trainee and manager views within 300 ms And no live order state is mutated
Partial Credit and Scoring Rules
Rule: If action types match AND order IDs match AND bays match AND |delta| <= T => score=1.0, reason=ExactMatch Rule: If action types match AND order IDs match AND bays differ AND |delta| <= T => score=weight.wrongBay (default 0.5), reason=WrongBay Rule: If action types match AND order IDs match AND T < |delta| <= (T + graceMs) => score=weight.lateWithinGrace (default 0.75), reason=LateWithinGrace Rule: If action types differ OR order IDs differ OR |delta| > (T + graceMs) => score=0.0, reason=NotEquivalent Rule: All scores are clamped to [0,1] and persisted with reason codes and matched event IDs
Sequence Adherence and Penalties
Given an expected staff step sequence S for order O And trainee steps T1..Tn are recorded in Shadow Mode When the comparator aligns trainee steps to S Then any trainee step occurring before its prerequisite staff step is marked OutOfSequence and penalized by weight.outOfSequence (default -0.25) with floor at 0.0 And any expected step with no trainee counterpart by order close is marked Missed with score=0.0, reason=Missed And order-level adherencePct = (matchedInSequenceCount / expectedCount) is computed and stored
Out-of-Order and Late Event Reconciliation
Given staff or trainee events may arrive late or out of chronological order within reorderWindowMs When a backdated or updated event for order O is ingested Then the comparator re-evaluates matches for order O only, deterministically, within 500 ms And emits at most one final match record per event (no duplicates) And updates scores and aggregate metrics atomically And appends an audit entry capturing priorLinkId, newLinkId, and changeReason=Reconciled
Idempotency and Duplicate Event Handling
Given each event carries idempotencyKey and fingerprint When identical trainee actions (same idempotencyKey or identical fingerprint within 60s) are received Then only the first event is considered; subsequent duplicates are ignored without changing scores or metrics When a trainee edits an action within editWindowMs (e.g., changes bay) Then the latest version supersedes prior versions, triggers a single recomputation, and prior versions remain unscored And all comparator operations execute in the Shadow namespace and never mutate live order state
Metrics Computation and Surfacing
Given the comparator produces match results for a trainee during a shift When at least one match attempt exists Then metrics are computed: accuracyAvg (mean step score), latencyDeltaAvgMs, adherencePct, mismatchCount, nearMissCount And trainee UI highlights mismatches and near-misses inline on the shadow board within 500 ms of computation And manager view displays per-shift aggregates refreshed at least every 5 seconds And GET /shadow/comparator/metrics?shiftId={id} returns the metrics with p95 latency <= 300 ms
Configurable Tolerances and Scoring Weights
Given store-level settings exist for timeToleranceMs, graceMs, reorderWindowMs, editWindowMs, and weight.* keys When an admin updates any of these settings Then new values are validated, versioned, and applied to comparisons created after the change timestamp And historical comparisons are not retroactively modified And all services observe the new settings version within 2 seconds And missing/invalid settings fall back to defaults while logging a warning without blocking comparisons
Just-in-time Hints Engine
"As a trainee, I want contextual hints when I hesitate or diverge from SOPs so that I can correct course quickly without disrupting live operations."
Description

Deliver contextual, non-intrusive hints in Shadow Mode triggered by signals such as inactivity thresholds, predicted next best action, SLA risk (food cooling), or detected mismatch patterns. Hints include microcopy, SOP checklists, and gentle prompts positioned near relevant controls; all cues are confined to the shadow layer and never affect the live board or send SMS. Provide a configurable library by location/brand, adjustable aggressiveness by trainee stage, and A/B testing to measure acceptance and effectiveness. Track hint usage and outcomes to refine content over time and support localization as needed.

Acceptance Criteria
Inactivity-triggered contextual hint
Given I am a trainee viewing the Shadow Mode arrivals board And there has been no interaction with an actionable order card for 20 seconds And a predicted next-best action is available for that order When the inactivity threshold is reached Then a hint appears within 500 ms, anchored to the control for the predicted action And the hint contains concise microcopy (<= 140 chars) with one clear call to action And the hint is dismissible via click/tap on X or ESC and via keyboard focus-out And the hint will not redisplay for that order for 120 seconds or until state changes And recording occurs: impression, dismiss, accept if the suggested action is taken within 10 seconds of the hint
SLA risk and food-cooling hint
Given an order is within 2 minutes of SLA breach or cooling risk score >= 0.7 When the risk condition is detected Then a risk-level hint (warning or critical) appears within 300 ms with a countdown timer And the hint includes a link to the relevant SOP checklist And the hint does not occlude primary CTAs and occupies < 25% of the order card area And the hint auto-dismisses when the risk drops below threshold or the corrective action is completed And the event records include risk level, time-to-action, and resolution outcome
Mismatch pattern detection hint
Given Shadow Mode is comparing trainee actions to live staff actions on the same orders And the trainee selects a different bay assignment or fulfillment action than live staff When a mismatch of the same rule type occurs 3 times within a rolling 30-minute session Then a pattern hint is shown explaining the expected rule with a 'See why' link to SOP And the pattern hint offers a one-click 'Try recommended action' in the shadow layer only And subsequent similar events within the session are rate-limited (max 1 hint every 5 minutes) And the system records pre- and post-hint mismatch rates for that rule type
Non-intrusive and accessible hint presentation
Given any hint is displayed in Shadow Mode Then it renders in the shadow layer only with z-index below modals and above order cards And it is keyboard navigable (Tab/Shift+Tab), ESC dismissible, and screen-reader labeled with aria-live='polite' And it meets WCAG 2.1 AA contrast for text and focus indicators And no more than 2 hints are visible concurrently per user; additional hints are queued And hints can be repositioned by dragging without blocking primary CTAs And performance: first paint under 100 ms after trigger and no main-thread task > 50 ms attributable to hint rendering
Isolation from live board and messaging systems
Given Shadow Mode hints are active When any hint is displayed, accepted, or dismissed Then no write occurs to live order state, bay assignments, or staff consoles And no SMS/MMS/voice endpoints are invoked And all analytics are written to the training namespace with anonymized customer data And toggling 'Assist-On' cues does not change customer-facing timestamps or notifications And automated tests verify zero network calls to production messaging domains during hint interactions
Configurable library by brand/location and stage aggressiveness
Given a brand and location context and a trainee stage (New, Ramping, Assist-On) When the hint library is resolved Then content and rules are selected by precedence: location override > brand default > global default And administrators with Trainer or Admin role can create/edit/publish hint content and triggers And published changes propagate to active sessions within 5 minutes without reload And aggressiveness defaults are enforced: - New: show 80% of eligible triggers, frequency cap 1 per 15 seconds - Ramping: show 40% of eligible triggers, cap 1 per 30 seconds - Assist-On: show only high-severity hints (SLA/mismatch), cap 1 per 60 seconds And aggressiveness settings are configurable per location and auditable via change log
A/B testing, effectiveness measurement, and localization
Given trainees start a Shadow Mode session When experiment split is enabled Then trainees are bucketed deterministically by trainee_id into variants per configured ratios (e.g., 50/50), with bucketing logged And events captured per hint include: impression, accept, dismiss, time_to_action, outcome (resolved/unresolved), locale And reporting exposes acceptance rate and lift (Variant vs Control) with 95% confidence when sample-size thresholds are met And event delivery is at-least-once with p95 under 5 minutes to the analytics sink And hint microcopy is rendered in the session locale; if translation is missing, fallback order is location > brand > en-US And RTL locales render mirrored layouts maintaining anchor alignment to relevant controls
Training Progress Dashboard
"As a store manager, I want a dashboard summarizing trainee accuracy, speed, and adherence so that I can measure readiness and plan staffing."
Description

Offer manager-facing views that aggregate trainee performance across sessions: overall accuracy, average reaction times, adherence by workflow step (assign bay, ready, hand-off), and common error categories (wrong bay, premature ready, missed SMS). Provide trend charts, per-shift breakdowns, and drill-down to replay specific orders for coaching. Support location filters, role-based access, and export to CSV/PDF, plus optional weekly email digests. Enforce retention policies and PII masking so analyses remain compliant with privacy standards while still actionable.

Acceptance Criteria
Metrics and Trend Visualization
Given I am a Manager viewing the Training Progress Dashboard with a date range and one or more locations selected When the dashboard loads or filters change Then I see summary KPIs for overall trainee accuracy (% to 1 decimal), average reaction time (seconds), adherence rates per workflow step (assign bay, ready, hand-off), and counts and rates for error categories (wrong bay, premature ready, missed SMS) And metrics are computed as: accuracy = (trainee actions matching actual staff actions at the same step/order) / (total comparable trainee actions); average reaction time = mean seconds from prompt visible to trainee action; adherence per step = correct actions / total step actions; error rate per category = errors in category / total actions And each KPI has a tooltip showing its definition and calculation window And changes to filters update all KPIs within 2 seconds at p95 And trend charts display daily lines for accuracy, average reaction time, adherence per step, and error rates over the selected range with hover values and a previous-period comparison toggle And days with no data render as 0 with an empty-state annotation
Per-Shift Breakdown with Location Filters
Given shift schedules are configured per location and I have selected a date range and one or more locations When I view the Per-Shift breakdown table Then results are grouped by shift (start–end, location) showing orders count, accuracy %, average reaction time (s), adherence per step (%), and top error category for each row And overnight shifts spanning midnight are attributed to their configured start date And table supports sorting by any column and pagination for >50 rows And applying/removing location filters updates the table within 2 seconds at p95
Drill-Down Replay for Coaching
Given I click a trainee name, shift row, or trend datapoint When I open the order/session replay Then I see a timeline aligning trainee Shadow Mode decisions with actual staff actions, with mismatches highlighted and any system hints annotated And playback controls (play, pause, 1x/2x, scrub) are available and the initial replay loads within 2 seconds at p95 And PII is masked inline per policy (e.g., phone last 4 only, vehicle color retained, license plate last 3 only) And I can add a time-stamped coaching note that persists and is visible to Managers/Trainers for that trainee
Role-Based Access Control
Given users are assigned roles (Owner, Manager, Trainer, Trainee) and location scopes When a user accesses the Training Progress Dashboard Then Owners/Managers/Trainers can view aggregated metrics for locations they are scoped to And Trainees can only view their own session summaries without access to aggregate trends, exports, or other trainees' data And attempts to access unauthorized locations or views result in a 403 response and no data leakage And all access events are logged with user ID, role, timestamp, and location scope
Exports to CSV and PDF
Given I have applied filters, selected columns, and a date range When I click Export CSV Then a UTF-8 CSV with headers is generated using the current filters and sort order, with ISO 8601 timestamps including timezone, one row per shift/session, and unformatted numeric fields And for up to 10,000 rows the CSV is available within 15 seconds; otherwise a progress indicator and retry option are shown When I click Export PDF Then a PDF including summary KPIs, trend charts, and the per-shift table is generated with vector graphics and a file size ≤ 10 MB And exported data preserves PII masking rules and includes a header block listing applied filters and date range
Weekly Email Digest
Given a Manager or Owner has opted in to weekly digests and has at least one location in scope When it is Monday at 08:00 in the recipient’s primary location timezone Then an email is sent summarizing the previous week with accuracy %, average reaction time, adherence per step, and top error categories per location, plus deltas vs the prior week And the email includes deep links back to the dashboard with filters pre-applied, and working unsubscribe/manage-preferences links And no email is sent if there were zero sessions in the period And transient send failures are retried up to 3 times with exponential backoff and are logged
Retention and PII Masking Compliance
Given a data retention policy is configured (default 90 days) and PII masking rules are enabled When data exceeds the retention window Then raw replays and event-level PII are purged or irreversibly anonymized while aggregated metrics remain intact And masked fields in UI, exports, and digests include: phone (last 4 only), customer name (initial + last initial), license plate (last 3 only), and free-text notes scrubbed via pattern-based redaction And a daily compliance job runs at 02:00 local time logging job ID, records processed, records purged/anonymized, and any errors And requests for purged replays return a clear not-available message without exposing identifiers
Assist-On Cues for Graduates
"As a newly graduated staff member, I want subtle assist cues during my first live shifts so that I can perform confidently while I ramp up."
Description

Enable a lightweight assist overlay for newly certified users operating on the live board. Provide subtle visual cues such as suggested next action, risk badges for overdue pickups, and gentle reminders to confirm bay or vehicle match. Ensure cues are non-blocking, rate-limited, and dismissible, with hard guards that prevent automated changes or outbound SMS without explicit confirmation. Allow supervisors to configure duration (e.g., first N shifts) and auto-sunset based on performance thresholds, while collecting telemetry to evaluate impact on wait times and accuracy.

Acceptance Criteria
Assist overlay visibility and eligibility for graduates
Given a newly certified user with Assist-On enabled logs into the live arrivals board When the board loads Then an 'Assist-On' indicator is visible in the header and subtle overlay cues are enabled for that user And non-eligible users (no Assist-On) do not see the indicator or cues And a per-user toggle is visible only if supervisor policy allows toggling
Contextual suggested actions and risk badges
Given an order card with a detected vehicle arrival and no bay assigned When the arrival is registered Then a suggested 'Assign bay' cue appears within 2 seconds on that order card Given an order card with a bay assigned and a vehicle/bay mismatch detected When the mismatch is detected Then a 'Confirm bay/vehicle match' reminder cue appears on that order card Given an order card is overdue When promised pickup time is exceeded by ≥5 minutes OR the customer has waited at the bay ≥3 minutes Then a 'Overdue' risk badge appears on that card until the condition clears
Cues are non-blocking and dismissible
Given any assist cue is displayed When the user interacts with other board controls Then no interaction is blocked by the presence of the cue Given a cue is displayed When the user clicks Dismiss (X) Then the cue hides immediately and does not reappear for that order for at least 10 minutes or until the underlying state changes materially (e.g., new arrival, new mismatch, overdue threshold increases) And the dismiss action is recorded in telemetry
Rate limiting and density of assist cues
Given assist cues are generated When multiple cues are eligible Then no more than 1 new cue is introduced per user every 10 seconds And no more than 3 cues are simultaneously visible per user; additional cues are queued And for a given order, the same cue type will not be shown more than once within a 60-second cooldown window
Hard guards for outbound SMS and state changes
Given a suggested action would send an SMS or change order state (e.g., reassign bay, send arrival nudge) When the user clicks the suggested action Then a confirmation dialog summarizes the exact change and recipient(s) And no SMS is sent and no state changes occur until the user explicitly confirms And clicking Cancel or closing the dialog results in no changes and the cue remains or is rescheduled per rate limits And system logs show zero automated outbound messages or state changes without a recorded user confirmation event
Supervisor-configured duration and auto-sunset
Given a supervisor opens the Assist-On settings When they configure duration as the first N shifts for a user or role (integer, default 5) Then Assist-On is automatically enabled for that user during their next N completed live shifts and disabled afterward unless overridden Given performance thresholds are configured (default: last 3 shifts average pickup wait time ≤ 8 minutes AND bay/vehicle match confirmation rate ≥ 98%) When a graduate meets both thresholds Then Assist-On auto-disables at the next login, a notification is sent to the supervisor, and the change is audit-logged And supervisors can manually re-enable or extend Assist-On for specific users
Telemetry and impact measurement
Given Assist-On is active for a user When cues are shown, dismissed, or accepted Then telemetry records event type, timestamps, order ID, user ID, and subsequent action outcomes Given sufficient data is collected When the supervisor views the Assist-On report Then the dashboard shows aggregated impact metrics (e.g., delta in average wait time, bay/vehicle match accuracy, cue acceptance and dismissal rates) comparing periods with vs. without Assist-On And CSV export includes per-shift aggregates And telemetry events are available in dashboards within 5 minutes of occurrence
Role-based Access and Safety Controls
"As an administrator, I want granular permissions and safety controls for Shadow Mode and Assist-On so that trainees can learn safely and data remains compliant."
Description

Introduce granular permissions to enable Shadow Mode, view training analytics, and activate Assist-On. Default trainees to shadow-only with no live edit rights; supervisors can monitor both shadow and live panes. Provide secure session start/end controls, auto-timeouts, single-click kill switches, and SSO integration where available. Maintain comprehensive audit logs for permission changes and session activity, and enforce PII masking in training contexts to protect customer data received via SMS.

Acceptance Criteria
Trainee Default Shadow-Only Access
- Given a user with role Trainee, When they sign in, Then only the Shadow Mode pane is interactive and all live controls are disabled. - Given a Trainee attempts any live action (e.g., assign bay, mark order ready, edit arrival), When the action is submitted, Then the system blocks the change, shows "Insufficient permissions", and writes an audit entry (user, action, timestamp, resource). - Given a Trainee performs a shadow action, When it is submitted, Then the action is stored only in the shadow layer and does not alter live order state. - Given a Trainee without "View Training Analytics" permission, When they attempt to access Training Analytics, Then access is denied and the attempt is logged.
Supervisor Dual-Pane Monitoring
- Given a Supervisor with "Monitor Shadow and Live" permission, When opening the arrivals board, Then both Live and Shadow panes are available (side-by-side or toggle) and reflect live updates within 2 seconds. - Given the Supervisor acts on the Live pane, When an action is performed, Then the live state updates successfully and an audit entry is created. - Given the Supervisor enables Comparison view, When viewing an order, Then differences between shadow decisions and live actions are highlighted per order. - Given the Supervisor switches between panes, When filters/time range are set, Then the same context persists across panes.
Assist-On Activation and Timebox
- Given a user marked Graduate with "Assist-On" permission, When starting a live session, Then the Assist-On toggle is visible and off by default. - Given Assist-On is enabled, When operating on the Live pane, Then assist cues appear as hints and no automatic state changes occur without explicit user action. - Given the org limit is 3 shifts by default (configurable), When the Graduate completes 3 live sessions, Then Assist-On becomes unavailable until reauthorized by a Supervisor. - Given a user without "Assist-On" permission, When attempting to enable Assist-On, Then the control is hidden/disabled and any attempt is logged.
SSO Login and Role Mapping
- Given SSO is enabled for the organization, When a user authenticates via OIDC or SAML 2.0, Then the user is signed in and their role is assigned via configured group/claim mapping. - Given the IdP provides no recognized mapping, When the user signs in, Then the account is provisioned with least privilege (Trainee) and an admin notification is generated. - Given an SSO logout at the IdP, When the event is received, Then the application session terminates within 10 seconds. - Given SSO is unavailable and local login is disabled for the org, When a user attempts to sign in, Then access is denied with guidance to contact an admin.
Secure Session Controls and Auto-Timeout
- Given any user, When Start Session is clicked, Then a session token is issued and session start is recorded in audit logs. - Given 15 minutes of inactivity (configurable 5–60), When the timer elapses, Then the session auto-times out, requires re-authentication, preserves unsaved shadow work, and ends live operations gracefully. - Given a Supervisor triggers the Kill Switch for a user, When confirmed, Then the target session terminates within 5 seconds and an audit entry with reason is recorded. - Given a user ends their session, When End Session is confirmed, Then they are signed out and all active tokens for that session are invalidated.
Audit Logging for Permissions and Sessions
- Given an admin changes a user's role or permissions, When the change is saved, Then an audit record is written capturing actor, target, before/after values, reason (required), timestamp (UTC), IP, and org. - Given session lifecycle events (start, end, timeout, kill), When they occur, Then each event is logged with user, method (manual/auto/SSO), timestamp, and client information. - Given a Supervisor with "View Audit" permission, When requesting logs for a date range, Then results include the fields above, are filterable, and are exportable to CSV. - Given a 365-day retention policy, When logs older than 365 days are requested, Then the system returns a retention notice and no data beyond the retention window.
PII Masking in Training Contexts
- Given any Shadow Mode or Training Analytics view, When displaying customer data from SMS, Then phone numbers are masked to last 4 digits, names to first initial plus "***", and emails to "f***@d***.tld" (or equivalent masking rules). - Given a Trainee user, When viewing any screen, Then PII remains masked and cannot be unmasked. - Given a Supervisor, When viewing Training Analytics or Shadow panes, Then PII remains masked; When viewing the Live pane, Then full PII is visible per org policy. - Given API endpoints serving training/shadow contexts, When returning customer fields, Then masked values are returned by default and unmasked values are not included.

Skill Auto‑Tune

The simulator spots patterns—like slow bay assignment or overusing overrides—and automatically adjusts upcoming drills to target those gaps. Personalized practice accelerates mastery and saves trainer time while aligning skills with real‑world demand at each location.

Requirements

Skill Signal Capture & Pattern Detection
"As an ops trainer, I want the system to automatically detect skill gaps from real usage so that drills target the highest-impact weaknesses without manual analysis."
Description

Continuously capture and aggregate simulator and live-operation signals (e.g., bay assignment latency, override frequency, reassignment events, missed/late pickups, SMS response lag) to identify skill gaps by user, role, shift, and location. Establish baselines and rolling windows to quantify variance from expected behavior and derive a per-skill gap score. Provide both near–real-time scoring (for immediate drill selection) and daily batch recomputation (for stability), with tunable thresholds and confidence levels. Integrate with CurbPing’s existing event stream without requiring apps or hardware; respect SMS-first workflows. Enforce privacy by storing pseudonymous identifiers, configurable retention, and role-based access. Emit structured "gap vectors" that downstream components consume to drive targeted practice.

Acceptance Criteria
Real-Time Gap Scoring from Event Stream
- Given CurbPing emits operational events (arrival_detected, bay_assigned, override_used, reassigned, pickup_missed, pickup_late, sms_received), When the last contributing event for a skill metric in a session is received, Then a provisional gap score is computed and persisted within 5 seconds P95 and 10 seconds P99 of that event. - Given duplicate or out-of-order events up to 2 minutes late, When processed, Then scoring is idempotent with no double counting and final scores converge within 1% of the canonical result. - Given a successful computation, When the score is produced, Then a gap_vector is emitted to the event bus and is retrievable via API within 10 seconds with fields score, confidence, sample_size, and config_version populated. - Given sustained load of 30 orders/hour/location across 100 concurrent locations, When processing real-time scoring, Then end-to-end error rate is ≤0.1% and the service maintains P95 latency ≤5 seconds.
Daily Batch Recompute and Stability
- Given the daily batch window 00:00–04:00 local time per location, When the batch job runs, Then it recomputes gap scores for the prior 7 calendar days and writes results tagged with batch_version and processing_date. - Given a rerun for the same date range and configuration, When executed, Then outputs are idempotent and match the previous run within an absolute difference ≤0.5 on the 0–100 gap score scale. - Given a backfill request for a historical date range, When executed, Then gap_vectors are recomputed and emitted with the correct effective baseline_version and config_version and without duplicating existing records. - Given a failure mid-run, When the job restarts, Then it resumes from the last successful partition with exactly-once semantics for outputs.
Baselines, Rolling Windows, and Variance
- Given ≥30 qualifying events for a user-role-shift-location in the last 7 days, When computing baselines, Then the cohort-specific baseline is used; otherwise fall back to role+location baseline; if still insufficient, fall back to tenant-wide baseline. - Given a baseline and a current rolling window, When computing the gap, Then the system produces absolute variance and a normalized gap score in [0,100] along with a z_score and includes them in the gap_vector. - Given insufficient data at all fallback levels, When scoring, Then emit a gap_vector with score=null, confidence=0, and reason=insufficient_data without blocking downstream processing. - Given observed drift exceeding the configured threshold across 3 consecutive windows, When detected, Then the baseline auto-updates and baseline_version increments and is reflected in subsequent outputs.
Tunable Thresholds and Confidence Controls
- Given an authorized Admin, When updating thresholds via the Config API (min_sample, latency_target_ms, override_rate_target, drift_threshold, window_sizes, min_confidence), Then changes are validated, versioned, audited (actor, timestamp, diff), and applied within 5 minutes to real-time scoring and at the next run to batch. - Given an active threshold set, When computing scores, Then confidence ∈ [0,1] is calculated using the configured method and included; if confidence < min_confidence, Then the score is flagged provisional=true in the gap_vector. - Given an invalid configuration payload, When submitted, Then the API responds 400 with field-level errors and no partial updates are applied. - Given a rollback request to a prior config_version, When executed, Then that version is activated within 2 minutes and subsequent computations use it; a change event is emitted and recorded in the audit log.
Privacy: Pseudonymous IDs, Retention, RBAC
- Given incoming events containing phone numbers or staff emails, When persisted for analytics, Then no direct identifiers are stored; instead, pseudonymous subject identifiers (salted hash or surrogate key) are used and no raw PII appears in analytics tables or emitted gap_vectors. - Given a retention policy per signal type (default 90 days unless configured), When records exceed their TTL, Then they are hard-deleted within 24 hours and deletions are logged with counts and ranges. - Given role-based access controls (Admin, Trainer, Manager), When users query gap data, Then access is restricted to authorized scopes (assigned locations/roles) and PII redaction is enforced. - Given an out-of-scope access attempt, When evaluated, Then the system returns 403 and writes an access_log entry with actor_id, scope_requested, and reason=rbac_denied.
SMS-First Signal Capture (No App/Hardware)
- Given a customer clicks the SMS 'I'm here' link or replies with supported keywords, When the event is received, Then arrival timestamps and bay selection interactions are captured without requiring any app installation or hardware. - Given an SMS reply without link click, When processed, Then sms_response_lag is measured from message send to reply time and associated to the correct order via pseudonymous keys. - Given intermittent connectivity causing up to 5-minute delays, When events arrive late, Then ingestion accepts them, orders events using server timestamps, and applies de-duplication rules. - Given staff actions in the web console (assign, reassign, override), When performed, Then signals are captured with actor role and location context and included in scoring windows.
Structured Gap Vector Emission and Contract
- Given a computed score, When emitting a gap_vector, Then the payload conforms to schema v1 with required fields: subject_id, role, location_id, shift_id, skill_id, window_start, window_end, score [0–100] or null, confidence [0–1], sample_size, baseline_version, config_version, contributors[], reason?, emitted_at. - Given a need for schema changes, When a breaking change is introduced, Then a new schema version is published and both versions run concurrently for ≥30 days with feature-flagged routing. - Given downstream Skill Auto‑Tune subscriptions, When gap_vectors are emitted, Then ≥99% are acknowledged by consumers within 15 seconds and retries/backoffs are observable. - Given a malformed payload, When validation runs, Then publishing is rejected, an alert is raised within 1 minute, and no invalid messages reach downstream consumers.
Adaptive Drill Generator
"As a frontline staffer, I want practice scenarios that mirror my toughest situations so that I improve faster on the skills I struggle with most."
Description

Generate personalized practice drills that target detected gaps using a configurable skill taxonomy (e.g., bay assignment speed, override judgment, mispark resolution, multi-order sequencing). Map gap scores to parameterized scenarios (traffic load, order complexity, bay availability, customer communication tone) and adapt difficulty over time based on performance. Compose sessions with a balanced mix of gap-focused, maintenance, and stretch drills, ensuring variety to prevent memorization while remaining reproducible for auditing. Respect operational constraints (short sessions, mobile/browser delivery, SMS entry points) and produce a machine-readable session plan consumed by the simulator UI.

Acceptance Criteria
Gap-to-Drill Mapping via Configurable Skill Taxonomy
Given a skill taxonomy configuration (taxonomy_version="v1") defining gap score buckets {high: >=0.70, medium: 0.40–0.69, low: <0.40} and parameter ranges per bucket When a gap profile with per-skill scores is provided to the generator Then each generated drill includes targeted_skill referencing the taxonomy, and all drill parameters (traffic_load, order_complexity, bay_availability, communication_tone) fall within the configured ranges for that skill’s bucket And then the session metadata includes taxonomy_version="v1" and the list of targeted skills And then for every skill with score >=0.40, at least one drill is included unless constrained by session length, in which case drills prioritize the highest scores and the rationale notes any deferred skills
Adaptive Difficulty Progression Based on Recent Performance
Given recent performance metrics per skill over the last 10 targeted drills (success_rate, avg_time_s, hint_usage) When generating the next session Then for any skill with success_rate >= 85% and avg_time_s within target, increase difficulty by +1 level (max +1 per session) And then for any skill with success_rate < 60% or avg_time_s > target, decrease difficulty by -1 level (max -1 per session) And then if a difficulty change occurred in the previous session for the same skill, do not change it again unless success_rate is <= 50% or >= 95% (anti-oscillation) And then difficulty remains within the allowed range for the scenario_template
Balanced Session Composition with Variety and Anti-Memorization
Given a target session length L drills and the user’s ranked gap profile When composing the session Then the mix is gap-focused 50–70%, maintenance 20–40%, and stretch 10–20% of L (rounded to integers), with at least one of each type when L >= 3 And then no two consecutive drills have the same scenario_template_id with identical parameters And then at least 3 unique scenario_template_id are included when L >= 5 And then no drill is an exact parameter duplicate of any drill from the user’s last 3 sessions And then the session plan includes session_seed and per-drill seed such that regenerating with identical inputs yields an identical plan
Operational Constraints: Short, Mobile-First, SMS Entry
Given organizational constraints for short sessions and mobile/browser delivery When generating the session plan Then estimated_duration_minutes <= 8 based on the sum of drill time_limit_seconds and expected buffers And then the plan declares delivery_modes including ["mobile_web"] and excludes any native_app or external_hardware requirements And then the plan includes an entrypoint of type "sms" with an HTTPS launch_url and token length <= 32 characters And then all drill interactions specified are compatible with mobile browsers (no requirements for keyboard-only shortcuts or hover-only interactions)
Machine-Readable Session Plan Schema for Simulator UI
Given JSON Schema session_plan_schema version "sp-1.0" When the generator outputs a session plan Then the plan validates against sp-1.0 and includes required fields: session_id (UUIDv4), user_id, location_id, generated_at (ISO8601), taxonomy_version, session_seed, version, drills[] And then each drill includes: drill_id (UUIDv4), seed, targeted_skill, category, difficulty, scenario_template_id, parameters {traffic_load: 0–100, order_complexity: 1–5, bay_availability: 0–10, communication_tone: one of [neutral, apologetic, upbeat, stressed]}, time_limit_seconds, scoring_rubric_id, rationale And then the total JSON payload size <= 100 KB And then the simulator UI can consume the plan without transformation (no missing required fields)
Deterministic Reproducibility and Audit Trail Without PII
Given the same inputs (taxonomy_version, gap profile, config, session_seed, user_id, location_id) When the generator is re-run Then the produced JSON is byte-identical to the prior plan And then the plan contains an audit section with generation_config_hash, mapping_rules_version, and per-drill rationale linking parameters to gap scores/buckets And then no PII (names, phone numbers, messages) is present anywhere in the plan; only user_id and location_id identifiers are included And then a SHA-256 checksum of the plan is provided in metadata for audit integrity
Personalization Engine & Mastery Tracking
"As a shift lead, I want training to adapt to each employee’s current level and our location’s demand so that new hires ramp quickly and veterans keep sharpening relevant skills."
Description

Maintain a per-user profile that tracks mastery per skill using a transparent scoring model (e.g., ELO-style or Bayesian mastery), decay for recency, and learning-rate parameters. Adjust drill frequency and difficulty from this profile and from location-level demand (skills weighted by real incident frequency). Support role-aware targets (runner, expeditor, curb lead) and cross-shift continuity via magic-link authentication (no app/hardware). Provide safeguards to avoid abrupt difficulty spikes and to prevent overtraining on a single skill. Persist progress across devices and locations while honoring privacy and access policies.

Acceptance Criteria
Per-User Skill Mastery Scoring with Recency Decay
Given a user completes a drill attempt for skill S with outcome pass/fail and timestamp T When the system updates the mastery profile Then the mastery score for S is recalculated using the configured model (ELO or Bayesian) with a per-user learning-rate and a recency decay factor based on T And the update record stores pre-score, post-score, outcome, learning-rate, decay factor, model name, and timestamp And the mastery score remains within [0.0, 1.0] And reprocessing the same attempt does not change the final score (idempotent)
Role-Aware Targets Influence Drill Selection
Given a user has role R with target mastery thresholds per skill When generating the next 10 drills for the user Then at least 70% of selected drills target skills where the user’s mastery is below the role target And no drills are assigned for skills marked Not-Applicable for role R And each assigned drill includes a rationale referencing the role target delta for the selected skill
Location Demand Weighting Adjusts Skill Mix
Given location L has incident frequency weights per skill computed over the last 14 days When generating a batch of 50 drills for users at L Then the skill distribution across assigned drills matches the weights within ±10% per skill, subject to role targets And if incident data is unavailable, default network-wide weights are used and a fallback event is logged And weights are refreshed by 03:00 local time daily and affect subsequent scheduling
Safeguards Against Abrupt Difficulty Spikes
Given a user’s last 20 attempts yield a rolling success rate SR and a last difficulty level on a 1–10 scale for each skill When scheduling the next session within 24 hours Then the difficulty increase for any skill does not exceed +1 level compared to the last attempt for that skill And the scheduler targets a predicted success rate between 70% and 85%; if predicted SR < 60% for a candidate drill, the assigned difficulty is reduced by at least 1 level and a scaffold drill is inserted And each drill includes an explanation of difficulty selection referencing prior difficulty and SR
Overtraining Prevention and Spaced Practice
Given the last 20 assigned drills for a user When selecting the next drill Then no single skill constitutes more than 30% of drills in the rolling window unless an override is active for urgent demand or role-critical deficiency And the same skill does not appear in more than 2 consecutive drills; if it would, a different skill is selected And for skills with mastery ≥ 0.8, the inter-practice interval is at least 24 hours between successive assignments
Cross-Shift Continuity via Magic-Link Authentication
Given a user requests an SMS magic link to access their training When the user opens the link in a browser on any device Then the user is authenticated without installing an app or hardware and their profile, mastery scores, and scheduled drills load within 2 seconds at P95 on a typical 4G connection And the magic link is single-use, expires in 15 minutes, and cannot be reused across devices And revoking the session invalidates the token immediately; subsequent drill attempts require re-authentication and are attributed to the correct user
Persistence Across Devices and Locations with Privacy Controls
Given a user completes drills at Location A on Device 1 and later signs in at Location B on Device 2 When viewing their profile at Location B Then mastery scores, attempt history, and scheduling state reflect all prior activity And access controls prevent staff at Location B from viewing user-identifiable data from Location A beyond aggregated metrics And the user can export their personal training data and request deletion; upon deletion, future schedules exclude the user and historical records are pseudonymized within 24 hours And all profile data is encrypted in transit (TLS 1.2+) and at rest using AES-256 or equivalent
Real-time Feedback & Coaching Loop
"As a team member, I want clear, instant feedback on each drill so that I know exactly how to correct mistakes and get faster."
Description

Deliver immediate, actionable feedback within drills, including timing breakdowns, correct vs. chosen actions, and the rationale tied to CurbPing best practices. Offer micro-coaching tips, quick replays, and one-click retry of missed steps. Provide end-of-session summaries with trend lines, mastery deltas, and recommended focus areas. Ensure accessibility (clear language, optional audio prompts, mobile-friendly UI) and align tone with brand. Cache feedback for offline-friendly browser sessions initiated via SMS link.

Acceptance Criteria
Instant Step Feedback with Rationale and Timing
Given a user completes a drill step When the action result is submitted Then feedback renders within 250 ms of submission And shows the chosen action and the correct action side by side And includes a timing breakdown with reaction time and execution time in milliseconds And references the applicable CurbPing best practice by name and ID And provides one actionable coaching tip of 120 characters or fewer at ≤ 8th-grade reading level And copy passes brand tone lint rules (encouraging, concise, non-blaming) with ≥ 95% automated checks passing And the feedback record persists in the in-session history panel
One-Click Retry for Missed Steps
Given a user has a missed or incorrect step When they tap Retry Then the simulator restores the pre-step state within 500 ms without a full page reload And replays prerequisite context automatically if needed And allows a single reattempt that updates scoring and audit trail with versioning And disables Retry after a successful reattempt of that step
Quick Replay of Recent Actions
Given a drill step completed within the last 60 seconds When the user taps Replay Then an inline replay of the last 30 seconds of user actions and system responses plays at 1x speed And replay controls are keyboard-navigable and labeled for screen readers And captions and step annotations are displayed by default And the replay is available offline if its assets are cached
End-of-Session Summary with Trends and Recommendations
Given a user completes a drill session When the summary screen loads Then it displays trend lines for avg time-to-bay, override rate, and error rate over the last 5 sessions at the same location And shows mastery delta per skill area versus the prior session And lists the top 3 recommended focus areas with rationale and deep links to drills And the summary loads in ≤ 1 second on a 4G connection And a shareable summary link is generated that expires after 7 days
Accessible Feedback and Coaching UI
Given any feedback, tips, audio prompts, or summaries are displayed When evaluated against WCAG 2.1 AA Then color contrast ratios meet AA standards And all interactive elements have visible focus indicators and ARIA roles And audio prompts are optional, captioned, and user-togglable And all non-text elements have text alternatives And average reading level of feedback copy is ≤ 8th grade (English)
Mobile-Friendly Controls and Layout
Given the UI is viewed on mobile devices between 360x640 and 428x926 When the user interacts with feedback controls Then primary touch targets are at least 44x44 px with 8 px spacing And Retry, Replay, and Next are visible without scrolling in portrait And orientation change completes layout reflow in ≤ 100 ms without content loss And Lighthouse Accessibility ≥ 90 and Performance ≥ 70 on a mid-tier Android device
Offline Caching and Sync for SMS-Launched Sessions
Given an SMS deep link launches a drill and network connectivity drops When feedback is generated while offline Then text feedback, metrics, and replay thumbnails are cached locally And the user can continue the drill with no blocking errors And upon reconnection, cached data sync within 5 seconds without duplication And an unobtrusive indicator reflects offline/online state
Trainer Controls & Guardrails
"As a trainer, I want to configure and constrain auto-tuning so that practice aligns with policies and never disrupts live operations."
Description

Provide admin controls for trainers/managers to set learning objectives, weight skills, define blackout windows, cap daily practice time, and require approval for significant auto-tune changes. Enable policy rules (e.g., never auto-increase difficulty during peak hours, enforce minimum practice on safety-critical steps) and location-specific presets. Include an approval queue, audit logs of auto-tune decisions, rollback of session plans, and A/B toggles to measure impact. Enforce role-based access and configurable data retention to meet privacy expectations.

Acceptance Criteria
Role-Based Access Enforcement
Given a user with Trainer or Manager role, when they navigate to Trainer Controls, then the controls page loads and they can create, edit, and save settings. Given a user without the required role, when they attempt to access Trainer Controls via UI or API, then access is denied with HTTP 403 and no changes persist. Given a settings change is saved, when the operation completes, then the audit log records user ID, role, timestamp, and fields changed. Given RBAC policies, when requests are processed server-side, then enforcement is consistent across UI and API endpoints.
Learning Objectives & Skill Weighting Setup
Given a location, when a trainer sets 1–50 skills with weights that sum to 100%, then the configuration saves successfully and is versioned per location. Given invalid weights (sum not equal to 100% or any weight outside 0–100%), when saving, then the system blocks the save and shows field-level errors. Given a saved configuration, when the next session plan is generated, then planned drills reflect skill weights within ±5% tolerance across the plan. Given an objective change is saved, when sessions are already in progress, then those sessions are unaffected and the new version applies only to future sessions.
Practice Scheduling Guardrails (Blackouts, Peak Hours, Daily Cap)
Given configured blackout windows in the location timezone, when generating or updating session plans, then no drills are scheduled within blackout windows. Given a "never auto-increase difficulty during peak hours" rule and defined peak hours, when an auto-tune would increase difficulty during peak hours, then the increase is deferred until the next non-peak window. Given a daily practice time cap per trainee (e.g., 30 minutes), when a plan would exceed the cap, then drills beyond the cap are not scheduled and the plan indicates "cap reached". Given overlapping guardrails, when conflicts arise, then precedence is Blackout > Daily Cap > Difficulty Increase rule and the plan reflects the applied precedence.
Approval Workflow for Significant Auto-Tune Changes
Given thresholds for significant changes (difficulty delta ≥ 2 levels or skill mix change ≥ 20%), when auto-tune proposes such a plan, then the plan status becomes Pending Approval and is not applied. Given a pending plan, when an authorized trainer approves with a required comment, then the plan is applied and the approval is logged; when rejected, the prior plan remains active and rejection is logged. Given notification settings, when a plan enters Pending Approval, then trainers assigned to the location receive a notification within 1 minute. Given multiple pending items, when the approval queue is viewed, then items are sortable and filterable by location, trainee, submitted time, and change type.
Audit Logs & Data Retention for Auto-Tune Decisions
Given any auto-tune decision or manual override, when it occurs, then an immutable audit record is stored with timestamp (UTC), location ID, actor (system/user), inputs summary, rules triggered, before/after plan diff, and request ID. Given an audit search by date range, location, actor, or rule, when executed, then results return within 2 seconds for up to 10,000 records and are paginated. Given a data retention policy (configurable 30–365 days) for a location, when updated, then the new policy is saved and applies prospectively; records older than the retention window are purged within 24 hours and the purge is logged. Given an attempt to modify or delete an audit record directly, when executed, then the system prevents the change and logs the attempt.
Rollback of Auto-Tuned Session Plans
Given a plan history with at least two versions, when a trainer selects a previous version and confirms rollback, then the selected plan becomes active for future sessions within 1 minute. Given a rollback request that would violate current guardrails, when processed, then the rollback is blocked and the UI/API returns a message naming the blocking guardrail. Given a successful rollback, when applied, then an audit entry is created linking from the new active plan to the prior one with reason and approver. Given versioned configurations, when a rollback occurs, then the version number increments and the previous state remains viewable in history.
A/B Toggles to Measure Impact
Given A/B testing is enabled for a location with a defined allocation (e.g., 50/50), when eligible trainees start sessions, then they are deterministically assigned to A (control) or B (auto-tune) and remain in their cohort for the test duration. Given defined metrics (e.g., time to bay assignment, override rate, completion rate), when sessions complete, then metrics are attributed to the correct cohort and are available via report/API with cohort filters. Given the A/B toggle is disabled mid-test, when toggled off, then new sessions route to control while existing assignments remain unchanged; historical results remain queryable. Given permissions, when an unauthorized user attempts to change A/B settings, then the change is blocked and logged.
Performance Reporting & ROI Dashboard
"As an owner-operator, I want to see how training changes translate into wait-time reductions and labor savings so that I can justify the program and optimize staffing."
Description

Expose dashboards and exports showing mastery trends, drill completion, gap closure velocity, and correlations to operational KPIs (wait time, bay utilization, override rate, mispark incidents). Compare pre/post outcomes for cohorts and locations, estimate staff hours saved, and surface which drills produce the largest impact. Support scheduled email/CSV reports, role-based views, and privacy-preserving aggregation. Provide alerting when regression is detected or when a location’s skill mix diverges from demand.

Acceptance Criteria
Manager views mastery trends and exports CSV
Given a manager with access to Location A and a selected date range, when they open the Performance & ROI dashboard, then mastery trend and drill completion charts render scoped to Location A and the date range. Given filters (role, cohort, location, drill, date range) are applied, when Export CSV is requested, then the CSV includes columns: user_id_hashed, role, location_id, cohort_id, drill_id, date, mastery_score, completion_count and matches on-screen aggregations within ±0.1%. Then the export completes within 10 seconds for up to 100,000 rows and provides a download link that expires in 24 hours. And the charts load within 2 seconds at p95 for date ranges up to 90 days.
Analyst examines KPI correlations and pre/post outcomes
Given connected KPIs (wait_time, bay_utilization, override_rate, mispark_incidents), when Correlation is run for selected locations and date range, then the dashboard shows Pearson r and p-value between skill metrics (mastery, gap_closure_velocity) and each KPI with significance flags (p<0.05). Given a cohort and pre/post periods are selected, when Compare is executed, then the system displays absolute and percent deltas with 95% confidence intervals for KPIs and skills. Then users can download a correlation summary CSV with columns: metric_x, metric_y, r, p_value, n, location_scope, date_range. And all cards display data freshness timestamp and respect the selected timezone.
Operations leader configures ROI estimates and views hours saved
Given an Admin opens the ROI tab and inputs assumptions (avg_orders_per_day, wait_time_delta_minutes, hourly_wage), when Calculate is clicked, then staff hours saved per week and monthly cost savings are computed and displayed with formulas used. Given location-specific overrides exist, when the user switches location, then calculations use location coefficients and update within 2 seconds. Then ROI assumptions are saved per location with version history (timestamp, editor) and can be reverted. And hours saved reconcile with raw aggregates within ±1 minute using (wait_time_delta_minutes × orders)/60.
Trainer schedules weekly performance email/CSV reports
Given a trainer selects recipients (by role or email), a report template, frequency (weekly), delivery time, and timezone, when Schedule is confirmed, then recipients receive an email with subject "[CurbPing] Weekly Performance - {Location} - {YYYY-MM-DD}" containing an attached CSV and a secure link. Then attachment schema matches on-screen export; links require authentication and expire in 7 days. And a delivery log records status (Sent, Bounced, Opened, Downloaded) per recipient and is exportable. Users can pause, resume, or delete a schedule; changes take effect within 15 minutes.
System alerts on regression and skill-demand divergence
Given thresholds are configured (e.g., mastery 7-day decline >10%, wait_time p95 increase >15%), when any condition is met for a location, then an alert is generated within 10 minutes and delivered via in-app and email. Then the alert includes metric, baseline window, current value, delta, impacted locations, recommended drills, and a deep link to the relevant dashboard view. Alerts are deduplicated per metric/location within a 24-hour window; acknowledgments and resolutions are tracked with timestamps and actor. Users can adjust thresholds per location and run a simulate action that produces a preview without sending notifications.
Privacy and role-based access controls on dashboard and exports
Given role-based views (Admin, Trainer, Manager), when a user signs in, then they see only authorized locations and permitted metrics; no PII is displayed. Exports and dashboards enforce k-anonymity ≥5; subgroups with n<5 are suppressed or aggregated; user IDs are salted, non-reversible hashes. All exports/downloads are logged with user_id, timestamp, filter state, and IP; Admins can view and export audit logs. Data retention: export files auto-delete after 30 days; report schedules and logs purge after 365 days; values configurable by Admin.

Product Ideas

Innovative concepts that could enhance this product's value proposition.

Reply to Pay

Text customers a secure payment link at arrival to collect balances or upsell add-ons. Capture tips and curbside donations without touching the POS.

Idea

BaySmart Autocomplete

Auto-suggest the best bay based on live occupancy and quote time, then text turn-by-turn lot instructions. Cuts misparks and slashes handoff walking.

Idea

Magic Link Shift Login

Passwordless staff login via time-boxed SMS or email links for shared tablets. Enforces role-based access and auto-logout at shift end.

Idea

Accessibility Priority Ping

Guests flag mobility needs on check-in; system reserves nearest bays, enlarges on-screen alerts, and prompts runners for trunk-first handoff. Protects dignity and speed.

Idea

Surge Slot Shaper

Pre-shape pickup slots around game times or rush surges, then overflow to a satellite zone when bays fill. Smooths spikes and prevents lot gridlock.

Idea

GhostBoard Multibrand

One arrivals board that colors and filters by virtual brand and delivery app. Prevents hallway jams and missed pings in shared kitchens.

Idea

Two Minute Training

Built-in simulator fires staged arrivals, bay conflicts, and late orders with on-screen scripts. New hires practice on live-like data without risking guests.

Idea

Press Coverage

Imagined press coverage for this groundbreaking product concept.

P

CurbPing Launches App‑Free Curbside Platform to Cut Waits 42% for SMS‑First Restaurants

Imagined Press Article

Today, CurbPing announced the general availability of its app‑free curbside pickup platform built for SMS‑first independent restaurant operators. Designed to end phone‑tag, fix misparked cars, and keep food hot, CurbPing uses a simple “I’m here” browser link to auto‑detect arrivals, assign the right bay, and coordinate runners—without apps, beacons, or new hardware. Early users report up to 42% shorter waits and roughly one staff hour freed per 50 curbside orders, allowing teams to move more tickets with less chaos. Curbside should be fast, clear, and dignified. For too many operators, it’s anything but: ringing phones, crossed wires, cold food, and cars idling in the wrong spots. CurbPing replaces that friction with a single, guest‑friendly text and a live arrivals board that brings order to the lot. Guests tap the secure link, confirm vehicle or bay info, and follow CurbNav SMS directions with simple landmarks. Staff see a precise live tile for each arrival—status, bay assignment, readiness, and handoff notes—so the right runner is at the right car at the right moment. “Restaurants told us they didn’t want another app, another dongle, or another login,” said Ava Reynolds, CEO of CurbPing. “They wanted curbside that just works on day one. By putting everything in a smart SMS link and a single live board, we’re cutting noise, cutting steps, and cutting waits—without cutting corners on security or guest experience.” CurbPing’s lot intelligence coordinates guests and staff automatically. RightBay AI blends live occupancy, predicted dwell, quoted prep windows, walking distance from the staging door, and runner workload to suggest the optimal bay the first time. CurbNav SMS provides turn‑by‑turn lot guidance using plain‑language landmarks and a lightweight web map pin, while BaySwap Live detects last‑second conflicts and offers a one‑tap reassignment that keeps cars moving. ParkRight Ping nudges guests who stop near—but not in—their assigned bay, and BayHeat Overlay shows live utilization and slow‑turn hotspots so Captains steer arrivals where turnover is fastest. “Before CurbPing, our curbside line meant missed calls and cold fries,” said Deja Patel, Owner‑Operator of an independent fast‑casual concept in Phoenix. “Now my Curb Captain assigns bays with confidence, drivers park right the first time, and our kitchen times the fire to the live arrival. We’ve shaved minutes off every handoff and we’re seeing happier guests—and happier runners.” The platform’s payment flow is equally simple and secure. One‑Tap Wallets allow Apple Pay, Google Pay, or saved cards directly inside the SMS link—no app, no typing—with Smart Tip Nudges that suggest fair, context‑aware options. Instant Add‑Ons surface ready‑fast extras based on real‑time kitchen capacity, boosting average order value without delaying pickup. For complex orders, Split Pay lets guests divide balances across multiple cards or corporate/personal with two taps. Handoff Pre‑Auth pre‑authorizes at arrival and auto‑captures on handoff, while Instant Adjust empowers frontline staff to correct totals on the spot from the same thread. Link Lock binds each payment link to device and time‑window with OTP fallback, screening out fraud while keeping good‑guest checkout effortless. “Curbside commerce has to be invisible and trustworthy,” said Maya Ortiz, Head of Product Marketing at CurbPing. “We put the tap‑to‑pay right where the guest already is—in the arrival text—and we back it with pre‑auth, instant adjustments, and link security so teams spend less time running cards and more time running food.” CurbPing is built for real kitchen and front‑of‑house rhythms. Expo‑Fire Expediter times fire/hold decisions off real‑time pings and marks orders ready with a single tap. Curb Captain watches the live board, confirms vehicles, and coordinates runners using quick SMS replies. Owner‑Operator Champions configure bay counts, templates, and metrics in minutes. For multi‑unit teams, Roster Sync pulls schedules from tools like 7shifts, When I Work, or Toast to pre‑send magic links, while Smart Role Match puts each teammate on the correct screen at login. GeoShift Lock and Break Pause protect sessions on shared tablets, and Offline PIN provides a resilient fallback when SMS or Wi‑Fi hiccups. “Clock‑ins, permissions, and shared devices were our biggest headache,” said Miguel Santos, Shift Lead at a two‑unit taco concept in Texas. “With Smart Role Match and Tap‑to‑Elevate, we’ve ended account sharing without slowing the line. If we need a quick refund, a supervisor approves the action with one tap and we’re back to serving.” CurbPing deploys quickly thanks to Layout Wizard, which guides operators through mapping bays on a satellite or lot photo, setting the staging door, and calibrating geofences with a quick SMS test drive. Accurate layouts power smarter autocompletes from day one, even for pop‑ups and seasonal operations that need to spin up curbside in hours, not weeks. For supporters focused on accessibility and dignity, the platform includes Proximity Priority to favor closer, wider, or covered bays when available, plus a suite of options to save and honor guest preferences on future visits. Guests can opt in to Access Profiles so teams are discreetly briefed on needs like trunk‑first handoff, wider bays, or a low‑voice approach—no repeated explanations required. CurbPing is available today. Independent restaurants, ghost kitchens, stadium‑area operators, and seasonal pop‑ups can get started in under an hour, with onboarding guides and optional two‑minute training drills that simulate real‑world curbside moments without risking live orders. About CurbPing CurbPing is the SMS‑first curbside platform that auto‑detects arrivals and orchestrates handoffs—no apps, no hardware. Restaurants use CurbPing to eliminate phone‑tag and misparked cars, assign smarter bays, keep food hot, and cut waits. Teams save time with lean payments, role‑aware logins, and a live arrivals board that brings order to the lot. Media Contact CurbPing Communications press@curbping.com +1 (415) 555‑0137 www.curbping.com/press

P

CurbPing Unveils Accessibility‑First Curbside Suite to Make Every Pickup Dignified and Fast

Imagined Press Article

CurbPing today introduced a comprehensive Accessibility‑First Suite that helps restaurants deliver faster, more dignified curbside experiences for guests with mobility or sensory needs—without special‑handling calls or added friction. The new capabilities center on clear instructions, priority routing, and discreet, opt‑in preferences that travel with the guest from one pickup to the next, ensuring teams can do the right thing on the first try. “At curbside, small moments make a big difference,” said Ava Reynolds, CEO of CurbPing. “If a guest needs a wider bay, a trunk‑first handoff, or a closer approach path, the system should enable that gracefully—not require a phone call or a long explanation in the rain. We built this suite so dignity and speed reinforce each other.” The Accessibility‑First Suite is anchored by Access Profiles, a privacy‑respecting way for guests to save specific handoff preferences during check‑in. With one opt‑in, guests can note needs such as wheelchair space, wider bays, trunk‑first handoff, passenger‑side delivery, low‑voice/no‑knock, or carry‑to‑seat assistance. On subsequent visits, CurbPing discreetly applies those needs, pre‑filling instructions for staff and prioritizing the nearest suitable bay—without exposing unnecessary personal details on shared screens. To help guests reach the right place without stress, RampRoute Directions adds step‑by‑step approach guidance highlighting curb cuts, ramp paths, covered zones, and accessible entrances directly inside the SMS link. If a bay changes, the same lightweight link sends an accessible reroute instantly, pairing plain‑language landmarks with a precise map pin so misparks drop and phone‑tag disappears. BayGuard Reserve protects a small pool of closer, wider, or covered bays for accessibility‑flagged arrivals. RightBay AI and BaySwap Live honor these holds automatically, gently redirecting other guests to alternatives. Combined with Proximity Priority—which weights assignments for near‑door access and harsh weather—the system prevents lot circling and puts the right spaces in reach at the right time. Assist Tap equips guests with one‑tap options at check‑in to request the exact help they need—trunk‑first, door‑side, mobility aid, carry‑to‑seat, or leave‑at‑passenger. Captains see precise prompts on the live arrivals board, while runners receive a concise checklist with one‑tap acknowledgments. BoldBoard Priority elevates accessibility arrivals with high‑contrast, enlarged tiles and clear badges and can send a discreet chime to staff devices so nothing is missed even during a surge. FlexHold Window gives flagged orders a smarter prep and pickup grace window, protecting food quality and respecting guests who may need a few extra minutes to park or transfer. “CurbPing has turned what used to be awkward and ad‑hoc into a consistent, dignified flow,” said Alana Brooks, a service leader at a family‑style restaurant group and an early user of the suite. “Guests don’t have to repeat their needs, our team knows exactly what to do, and handoffs are smoother for everyone. We’re doing more of the right things, faster.” The Accessibility‑First Suite integrates seamlessly with CurbPing’s core lot intelligence. Guests still check in through a secure SMS link—no apps required—and receive CurbNav SMS guidance to their assigned bay or an accessible alternative. Captains and Expo see the same unified tile with readiness, bay, and assistance details, so they can time a precise meet‑up and avoid remakes. If a last‑second change occurs, BaySwap Live and ParkRight Ping keep the flow moving with friendly, automated updates and a one‑tap “I’ve moved” confirmation. CurbPing developed the suite with input from operators who run busy lots across cities, stadium districts, and hospital neighborhoods, where accessible bays are crucial and conditions change quickly. The result is a system that preserves privacy, reduces back‑and‑forth, and ensures priority needs are honored by default—not after a customer advocates twice. “Our goal is to make accessibility the standard, not the exception,” said Maya Ortiz, Head of Product Marketing at CurbPing. “By putting preferences in the SMS link and elevating cues on the live board, we make it easier for every staff member—new or experienced—to deliver the right help at the right time.” Availability and Getting Started The Accessibility‑First Suite is available today for all CurbPing customers. Setup takes minutes. Access Profiles can be enabled in the template editor; RampRoute Directions and BayGuard Reserve activate automatically once your lot layout is mapped in Layout Wizard. Proximity Priority, Assist Tap, BoldBoard Priority, and FlexHold Window appear in the arrivals board with a simple toggle. Managers can preview the guest experience from their own phones, and two‑minute QuickStart Drills walk teams through common scenarios before a live shift. About CurbPing CurbPing is the SMS‑first curbside platform that auto‑detects arrivals and orchestrates handoffs—no apps, no hardware. Restaurants use CurbPing to eliminate phone‑tag and misparked cars, assign smarter bays, keep food hot, and cut waits. The platform includes accessible routing, lean payments, lot intelligence, and role‑aware tools that help teams move more orders with confidence. Media Contact CurbPing Communications press@curbping.com +1 (415) 555‑0137 www.curbping.com/press

P

New Surge Intelligence from CurbPing Smooths Game‑Day Spikes with Event‑Aware Slots and Satellite Zones

Imagined Press Article

CurbPing today launched a surge‑management toolkit that helps restaurants ride game‑day and concert spikes without gridlock or cold food. The new capabilities—Event Sync, Elastic Slots, Satellite Router, Nudge Shift, Fire Curve, and Rush Replay—work together to shape pickup demand before a surge, adapt capacity minute‑to‑minute during the rush, and learn what to improve next time. “Stadium nights used to feel like a traffic jam with a kitchen attached,” said Event‑Rush Orchestrator Owen, an operator outside a major arena who participated in early trials. “With CurbPing, we pre‑shaped the curve, diverted overflow to a satellite zone, and hit our quoted times even at peak. My runners walked less, my bays turned faster, and our guests got hot food without idling.” It starts with Event Sync, which auto‑pulls nearby game, concert, school, and weather calendars to show operators what is coming and when. Owners approve or tweak a recommended curve in one tap, and Captains see a clear surge timeline on the live board. Elastic Slots then expands or compresses five‑minute pickup slots based on live kitchen throughput, runner capacity, and current dwell so quoted times stay honest. When primary bays near capacity, Satellite Router automatically diverts new arrivals to a pre‑mapped satellite zone, sending guests CurbNav SMS with simple landmarks and a colored zone badge, while the arrivals board highlights runner notes by zone. During crunch minutes, Nudge Shift sends friendly, optional SMS offers to move flexible arrivals by five to ten minutes, with configurable perks like a priority bay or a small add‑on. The system only targets orders that won’t impact food quality and always respects accessibility flags. Expo‑Fire Expediter follows Fire Curve guidance to align kitchen timing to the shaped arrival curve so orders finish as bays free up—fewer remakes, hotter food, tighter handoffs. “Great surge management looks calm and feels fair,” said Ava Reynolds, CEO of CurbPing. “We built this so kitchens, Captains, and guests stay in sync even when the parking lot is not. It’s a living system that spreads demand, protects the nearest bays for the guests who need them most, and documents what worked so next time is smoother.” After the rush, Rush Replay generates a concise breakdown: slot fill percentages, overflow triggers, dwell by zone, saved minutes from diversions, and on‑time performance. Multi‑unit operators can compare across locations and clone winning templates, while single‑unit shops get one‑tap recommendations that turn insights into action before the next event. The surge toolkit dovetails with CurbPing’s core features. RightBay AI continues to assign optimal bays based on live conditions. BaySwap Live resolves last‑second conflicts. ParkRight Ping nudges near‑miss arrivals into place. BayHeat Overlay visualizes slow‑turn hotspots so Captains can temporarily steer cars where turnover is healthiest. Meanwhile, the payments flow—One‑Tap Wallets, Smart Tip Nudges, Instant Add‑Ons, Split Pay, Handoff Pre‑Auth, and Instant Adjust—stays lean, so the curb doesn’t bottleneck on the card step when the clock is tight. “Event Sync helped us staff the right way for the 7 p.m. tip‑off,” said Marco Alvarez, a downtown GM who pilots CurbPing. “Elastic Slots kept quotes steady, Satellite Router prevented jams near the door, and Fire Curve synced expo with the arrivals curve. We cleared what used to be a 45‑minute crunch in 28 minutes with higher guest ratings.” Availability and Onboarding The surge‑management toolkit is available today for all CurbPing customers. Setup requires a quick pass through Layout Wizard to map primary bays and any satellite zones. Operators can preview the event curve on the arrivals board and test Nudge Shift messaging from their own phones. Two‑minute QuickStart Drills and role‑specific RolePath Tracks walk Captains and Expo through BaySwap overrides, zone routing, and SMS tone before the next big night. About CurbPing CurbPing is the SMS‑first curbside platform that auto‑detects arrivals and orchestrates handoffs—no apps, no hardware. Restaurants use CurbPing to eliminate phone‑tag and misparked cars, assign smarter bays, keep food hot, and cut waits, even during event surges. Media Contact CurbPing Communications press@curbping.com +1 (415) 555‑0137 www.curbping.com/press

P

CurbPing Debuts Ghost Kitchen Board to Tame Multibrand Pickups and Speed Driver Turnover

Imagined Press Article

CurbPing today introduced a purpose‑built toolkit for shared kitchens and multibrand concepts that wrangle simultaneous pickups into a single, calm flow. The new Ghost Kitchen Board combines brand‑aware visuals, unified driver handling, and door/shelf routing so dispatchers can keep hallways clear and drivers out the door faster. “Virtual brands succeed on throughput and clarity. The handoff is the moment of truth,” said Ghost Kitchen Dispatcher Gia, who helped shape the product during trials. “With BrandBands and SLA Watch, I see exactly which orders need attention across all our apps. Courier Stack consolidates multi‑order pickups into one tile so I brief a driver once, not five times. It’s night‑and‑day for our hallway.” BrandBands gives each virtual brand and delivery app a distinct color band and icon badge on a single arrivals board, making it easy to spot and filter a subset view with one tap. When a driver is collecting multiple orders across brands, Courier Stack detects the overlap automatically and collapses those orders into one unified tile with a combined checklist, consolidated payment/verification state, and precise shelf/door pick points. SLA Watch normalizes each delivery app’s pickup timers and ETAs into a single, clear countdown, flagging at‑risk orders with gentle escalations and suggesting reprioritization so ratings stay protected. DoorFlow Zones map brands and apps to dedicated pickup doors, shelves, or staging lanes and auto‑route arrivals accordingly. Clear, on‑screen lane indicators guide runners and drivers to the right spot the first time, reducing cross‑traffic, noise, and bottlenecks in shared corridors. PingBridge ingests order events from delivery apps and POS (email/API/webhook) and deduplicates them into a single live tile with reliable state transitions—Arriving, At Door, Handed Off—so nothing gets missed across brands. ShelfMap assigns each order a clear location label and shows it right on the tile so runners stage consistently and drivers grab without hunting. “Shared‑kitchen teams don’t have a second to waste,” said Ava Reynolds, CEO of CurbPing. “We designed the Ghost Kitchen Board so dispatchers can operate on one screen with one language, even when five platforms are talking at once. It’s calmer for staff, clearer for drivers, and faster for guests.” The Ghost Kitchen Board is tightly integrated with CurbPing’s curbside core for operations that mix driver pickups with on‑premise curb. RightBay AI and CurbNav SMS guide curbside guests to the correct lanes or doors, while BaySwap Live resolves last‑second conflicts. For payments and adjustments, One‑Tap Wallets, Handoff Pre‑Auth, Instant Adjust, and Link Lock keep the flow fast and secure when a walk‑in guest needs to settle a balance alongside app drivers. Operators can tailor the board to their footprint in minutes using Layout Wizard. Brands, shelves, and doors are mapped once and then applied automatically as orders ingest via PingBridge. For training, QuickStart Drills simulate multibrand surges, late drivers, and shelf conflicts using real layouts and templates. RolePath Tracks ensure dispatchers and runners see only the controls they need, while ScriptCoach Overlay suggests clear, brand‑safe phrasing when messaging drivers or guests. “Since we turned it on, we’ve cut hallway dwell and ‘Where’s my order?’ pings sharply,” said Jordan Lee, Kitchen Manager at a multibrand facility in Chicago. “Courier Stack means a driver leaves once with everything they need, and ShelfMap ended the scavenger hunt on our rack. We’re moving more orders without raising voices.” Availability The Ghost Kitchen Board is available today for all CurbPing customers operating shared kitchens, multibrand concepts, or hybrid curb‑and‑driver models. A guided setup maps shelves, doors, lanes, and brands in under an hour with Layout Wizard, and optional two‑minute drills get new hires ready before their first live shift. About CurbPing CurbPing is the SMS‑first curbside platform that auto‑detects arrivals and orchestrates handoffs—no apps, no hardware. From independent restaurants to multibrand facilities, CurbPing eliminates phone‑tag and misroutes, assigns smarter bays and doors, and keeps food hot while cutting waits. Media Contact CurbPing Communications press@curbping.com +1 (415) 555‑0137 www.curbping.com/press

P

Train in Two Minutes: CurbPing Rolls Out Built‑In Simulator to Onboard Curb Teams Faster

Imagined Press Article

CurbPing today announced an integrated training suite that onboards curbside teams in minutes and builds confidence before a single live order hits the board. The simulator—made up of QuickStart Drills, RolePath Tracks, ScriptCoach Overlay, Mistake Replay, Confidence Meter, Shadow Mode, and Skill Auto‑Tune—runs right on top of each location’s real lot layout and SMS templates so learning translates instantly to live shifts. “Great curbside is choreography,” said Training Turnkey Talia, an operations trainer who piloted the suite. “Before, we explained the dance. Now we practice it. Two‑minute drills simulate exactly what a new Curb Captain or Expo will see: staged arrivals, bay conflicts, late orders, and add‑ons. ScriptCoach suggests the right quick reply in the right tone. People show up shift‑ready.” QuickStart Drills are bite‑sized, auto‑sequenced practice modules that fire your top curbside moments in a safe sandbox. Trainees experience the same live arrivals board they’ll use on shift, including RightBay AI suggestions, BaySwap prompts, ParkRight nudges, and payment flows. RolePath Tracks assemble role‑specific journeys for Curb Captain, Expo‑Fire Expediter, Ghost Kitchen Dispatcher, and more, loading only the screens and decisions that matter for that station and progressing from basics to advanced moves like overrides and Instant Adjust. ScriptCoach Overlay provides adaptive, on‑screen guidance for tone, clarity, and accessibility. Trainees preview exactly what guests see via SMS and learn why certain phrasing speeds handoffs and preserves dignity. After each drill, Mistake Replay shows a tappable timeline of actions, click‑slow heatmaps, and an ideal path comparison, with projected impacts on dwell time, reroutes, and tip conversion. A one‑tap Do Over cements the fix through repetition. The suite quantifies readiness without creating pressure. Confidence Meter scores real‑time proficiency across speed, accuracy, and guest empathy, rolling up to a simple Shift‑Ready badge that managers can trust. Shadow Mode adds a ghost layer over the live arrivals board so trainees can make parallel decisions that affect nothing while the system compares choices to actual staff actions and offers just‑in‑time hints. As patterns emerge—like slow bay assignments or overusing overrides—Skill Auto‑Tune automatically adjusts upcoming drills to target those gaps, accelerating mastery and saving trainer time. “Turnover is a reality. We built this to make day one feel like day ten,” said Ava Reynolds, CEO of CurbPing. “When teams can practice on their real layout with their real scripts and see the impact of each tap, they adopt faster and make better decisions in the lot. That’s how you keep food hot, lines short, and guests coming back.” Because the simulator runs on top of CurbPing’s actual configuration, setup is simple. Layout Wizard ensures bay maps, staging doors, and geofences are accurate. Managers can select featured drills based on their rush pattern—stadium surges, dinner peaks, pop‑up traffic—and the system will queue modules accordingly. For multi‑unit operators, training content can be standardized across locations while still tuning to each site’s geometry and staffing pattern. The training suite complements CurbPing’s workforce and security tools. Roster Sync pre‑sends magic links before each shift and starts sessions at clock‑in, Smart Role Match puts teammates on the correct screens automatically, and Tap‑to‑Elevate enables temporary higher access for actions like refunds with supervisor approval. GeoShift Lock, Offline PIN, and Break Pause maintain privacy and control on shared tablets, even during outages or high‑turnover days. Early users are seeing real impact. “Our new Captains reach Shift‑Ready in under an hour,” said Priya N., a general manager at a three‑unit Mediterranean concept. “Mistake Replay is a game‑changer—it turns a miss into a quick win. And Shadow Mode lets us train during service without risking a single order.” Availability The training suite is available today for all CurbPing customers. Managers can enable QuickStart Drills and RolePath Tracks from the Admin panel and send trainees a practice link via SMS. Most teams complete their first drills in under 10 minutes and carry that muscle memory into the next rush. About CurbPing CurbPing is the SMS‑first curbside platform that auto‑detects arrivals and orchestrates handoffs—no apps, no hardware. With integrated training, lot intelligence, accessible routing, and lean payments, CurbPing helps restaurants cut waits, keep food hot, and free staff time. Media Contact CurbPing Communications press@curbping.com +1 (415) 555‑0137 www.curbping.com/press

Want More Amazing Product Ideas?

Subscribe to receive a fresh, AI-generated product idea in your inbox every day. It's completely free, and you might just discover your next big thing!

Product team collaborating

Transform ideas into products

Full.CX effortlessly brings product visions to life.

This product was entirely generated using our AI and advanced algorithms. When you upgrade, you'll gain access to detailed product requirements, user personas, and feature specifications just like what you see below.