# RevHero QA Run — scheduled-20261018T120053 **Started:** 2026-10-18T12:00:57.952Z **Finished:** 2026-10-18T12:04:47.767Z ## Summary | | Count | |---|---| | Total | 494 | | PASS | 484 | | FAIL | 0 | | SKIP | 0 | | NOT_EXEC | 10 | ## Results | ID | Status | Duration | Description | |---|---|---|---| | `ANALYTICS-001` | PASS | 361.4977039999999ms | Unauth GET /v1/dashboard/health rejected | | `ANALYTICS-002` | PASS | 5191.225926000001ms | Authed GET /v1/dashboard/health does not fail closed (service up) | | `ANALYTICS-003` | PASS | 120.47724999999991ms | Bogus sibling path 404s distinctly from the real route's 401 (route-exists sanity) | | `FE-ACT-001` | PASS | 2.2419929999996384ms | Outbound SMS produces 'SMS Sent' activity (smoke) | | `FE-ACT-002` | PASS | 0.6698999999998705ms | Inbound SMS produces 'SMS Received' activity (smoke) | | `FE-ACT-003` | PASS | 1.015255000000252ms | Email sent produces 'Email Sent' activity (smoke) | | `FE-ACT-004` | PASS | 0.34742800000003626ms | Email received produces 'Email Received' activity (smoke) | | `FE-ACT-005` | PASS | 0.3408070000000407ms | A2P brand approved produces activity (smoke) | | `FE-ACT-006` | PASS | 0.5627009999998336ms | Phone purchase produces 'Phone Number Purchased' (smoke) | | `FE-ACT-007` | PASS | 0.24569899999960398ms | Toky import produces 'Phone Number Imported' (smoke) | | `FE-ACT-008` | PASS | 0.20340099999975791ms | Deal-loss negative-sentiment activity (smoke) | | `FE-ACT-009` | PASS | 0.22162400000024718ms | Stage processing failure activity (smoke) | | `FE-ACT-010` | PASS | 4995.430698ms | Activities mark-read endpoints respond | | `FE-ACT-011` | PASS | 0.4927289999995992ms | Activity feed scoped to current account_id (smoke) | | `FE-ACT-AIC-001` | PASS | 6022.181479999999ms | POST /v1/stages/:id/actions/ai-call endpoint exists | | `FE-ACT-AIC-002` | PASS | 0.5379639999991923ms | PUT updates AI-call action (smoke) | | `FE-ACT-AIC-003` | PASS | 3.204200000000128ms | DELETE removes action (smoke) | | `FE-ACT-AIC-004` | PASS | 0.3491619999986142ms | Trigger requests AI call (smoke) | | `FE-ACT-AIC-005` | PASS | 0.21837700000105542ms | Empty prompt_template → validation error (smoke) | | `FE-ACT-BBS-001` | PASS | 6586.510692ms | Endpoint exists | | `FE-ACT-BBS-002` | PASS | 0.6204790000010689ms | CRUD verified (smoke) | | `FE-ACT-BBS-003` | PASS | 0.23656099999789149ms | Trigger dispatches request (smoke) | | `FE-ACT-PD-001` | PASS | 3879.533593999997ms | Endpoint exists | | `FE-ACT-PD-002` | PASS | 0.5610969999979716ms | Editor lists folders/templates (smoke) | | `FE-ACT-PD-003` | PASS | 0.2181769999951939ms | CRUD verified (smoke) | | `FE-ACT-PD-004` | PASS | 2.0229360000012093ms | Trigger creates + emails doc (smoke) | | `FE-ACT-PD-005` | PASS | 0.30073199999606004ms | Invalid template_id → graceful failure (smoke) | | `FE-ACT-S2C-001` | PASS | 6168.775472999994ms | Endpoint exists | | `FE-ACT-S2C-002` | PASS | 0.48809100000653416ms | Editor lists user's campaigns + stages (smoke) | | `FE-ACT-S2C-003` | PASS | 0.22674299999926006ms | Trigger moves deal across campaigns (smoke) | | `FE-ACT-S2C-004` | PASS | 0.1940519999989192ms | Cycle prevention at save time (smoke) | | `FE-ACT-SF-001` | PASS | 4956.0734410000005ms | Endpoint exists | | `FE-ACT-SF-002` | PASS | 1.798437000004924ms | CRUD verified (smoke) | | `FE-ACT-SF-003` | PASS | 0.4556109999975888ms | Invalid API key at save → friendly error (smoke) | | `FE-ACT-SF-004` | PASS | 3.8404870000013034ms | Trigger logs activity (smoke) | | `FE-ACT-VM-001` | PASS | 16280.009727ms | POST /v1/stages/:id/actions/voicemail endpoint exists | | `FE-ACT-VM-002` | PASS | 0.6823739999999816ms | Voicemail editor accepts audio upload (smoke) | | `FE-ACT-VM-003` | PASS | 0.2446270000000368ms | PUT updates voicemail action (smoke) | | `FE-ACT-VM-004` | PASS | 0.2158930000005057ms | DELETE removes action (smoke) | | `FE-ACT-VM-005` | PASS | 0.2534430000014254ms | Trigger dispatches voicemail (smoke) | | `FE-ACT-VM-006` | PASS | 0.2695239999993646ms | Reject wrong file type with friendly error (smoke) | | `FE-ACT-VM-007` | PASS | 5.208842000000004ms | Reject oversized audio with friendly error (smoke) | | `FE-ADM-001` | PASS | 7499.317459ms | /admin/dashboard renders without crashing | | `FE-ADM-002` | PASS | 6.34849899999972ms | Sidebar shows Billing/Dashboard/Pricing/Campaigns (smoke) | | `FE-ADM-003` | PASS | 5345.395075999999ms | /admin/billing renders without crashing | | `FE-ADM-004` | PASS | 5300.400785ms | /admin/billing/clients renders without crashing | | `FE-ADM-005` | PASS | 5946.036228000001ms | /admin/billing/payments renders without crashing | | `FE-ADM-006` | PASS | 4131.344907999999ms | /admin/campaigns renders without crashing | | `FE-ADM-007` | PASS | 5452.952095000001ms | /admin/campaigns/analytics renders without crashing | | `FE-ADM-008` | PASS | 4907.618055999999ms | /admin/campaigns/templates renders without crashing | | `FE-ADM-012` | PASS | 3671.352595000004ms | /admin/pricing/plans renders without crashing | | `FE-ADM-013` | PASS | 4210.696382000002ms | /admin/pricing/plans/create renders without crashing | | `FE-ADM-014` | PASS | 3303.100778ms | /admin/pricing/promo-codes renders without crashing | | `FE-ADM-015` | PASS | 3783.3671170000016ms | /admin/pricing/addons renders without crashing | | `FE-ADM-016` | PASS | 3858.849485999999ms | /admin/user-settings renders without crashing | | `FE-ADM-017` | PASS | 5241.761404000004ms | MEMBER role hits /admin/dashboard → 403 or redirect | | `FE-ADM-018` | PASS | 0.4012689999944996ms | /admin/* on prod hostname → redirected (skip on staging) | | `FE-ADM-ADDON-001` | PASS | 5087.932949999988ms | Add-ons list renders | | `FE-ADM-ADDON-002` | PASS | 0.6598119999980554ms | Create requires name/price/scope (smoke) | | `FE-ADM-ADDON-003` | PASS | 0.2669590000004973ms | Created add-on appears in public list (smoke) | | `FE-ADM-ADDON-004` | PASS | 0.24289199999475386ms | Update price reflects on /settings/manage-plans (smoke) | | `FE-ADM-ADDON-005` | PASS | 6.063076000005822ms | Delete blocked when subs active (smoke) | | `FE-ADM-PLAN-001` | PASS | 4601.480221999998ms | Plans list renders | | `FE-ADM-PLAN-002` | PASS | 1.1937880000041332ms | Create form validates required fields (smoke) | | `FE-ADM-PLAN-003` | PASS | 0.3555740000010701ms | POST /v1/admin/plans creates plan (smoke) | | `FE-ADM-PLAN-004` | PASS | 0.43559300000197254ms | PUT updates plan, public endpoint reflects (smoke) | | `FE-ADM-PLAN-005` | PASS | 0.20320999999239575ms | Delete with active subs → friendly block (smoke) | | `FE-ADM-PLAN-006` | PASS | 1.7076579999993555ms | Delete unused plan succeeds (smoke) | | `FE-ADM-PROMO-001` | PASS | 4896.34635800001ms | Promo list shows discount/expiry/usage | | `FE-ADM-PROMO-002` | PASS | 0.36818700000003446ms | Create 100% code applies on signup (smoke) | | `FE-ADM-PROMO-003` | PASS | 0.24648099999467377ms | Past-expiry code returns expired (smoke) | | `FE-ADM-PROMO-004` | PASS | 0.3919309999910183ms | Usage cap enforced (smoke) | | `FE-ADM-PROMO-005` | PASS | 0.20587399999203626ms | Delete cleanly removes (smoke) | | `FE-AH-001` | PASS | 0.5342670000027283ms | PUT /v1/active-hours/:id saves schedule (smoke) | | `FE-AH-002` | PASS | 0.48328200000105426ms | Stage actions defer outside active window (smoke) | | `FE-AH-003` | PASS | 2.5482259999989765ms | Saturday outside window → defer to Monday (smoke) | | `FE-AH-004` | PASS | 0.6284840000007534ms | PUT /v1/active-hours-preferences globally disables (smoke) | | `FE-AI-001` | PASS | 6638.420002ms | /automation-campaign/[id] shows AI personalization toggle | | `FE-AI-002` | PASS | 4.729015999999319ms | Toggle ON opens cost modal + persists (smoke) | | `FE-AI-003` | PASS | 0.7305130000004283ms | Toggle OFF flips back, banner disappears (smoke) | | `FE-AI-004` | PASS | 0.377093999999488ms | Test-Email button disabled when AI flag OFF (smoke) | | `FE-AI-005` | PASS | 0.30825499999991735ms | Personalised stage email differs from template (smoke) | | `FE-AI-006` | PASS | 0.3793180000011489ms | AI flag OFF sends literal template (smoke) | | `FE-AI-007` | PASS | 0.3934640000006766ms | AI ON + empty goal → fallback to template (smoke) | | `FE-AI-008` | PASS | 2.927443000000494ms | AI ON + empty offering → fallback (smoke) | | `FE-AI-009` | PASS | 0.8181560000011814ms | Stage SMS with AI ON personalises body (smoke) | | `FE-AI-010` | PASS | 1.2925720000002912ms | AI runs before merge-tag rendering (smoke) | | `FE-AI-011` | PASS | 0.2710350000015751ms | generate_ai_variants switch hidden in prod (smoke) | | `FE-AI-012` | PASS | 1417.7658949999986ms | ai-agent /health endpoint reports OpenAI connectivity | | `FE-AI-013` | PASS | 2.881808000000092ms | Inbound sentiment runs regardless of AI flag (smoke) | | `FE-AI-014` | PASS | 0.5156719999995403ms | Heuristic infers NEGATIVE on 'unsubscribe' fast (smoke) | | `FE-AI-015` | PASS | 0.26911199999995006ms | Heuristic falls through to OpenAI (smoke) | | `FE-AI-016` | PASS | 0.22825599999850965ms | OpenAI failure retries 5 times → sentiment NONE (smoke) | | `FE-AI-017` | PASS | 225.07672600000114ms | sentiment-webhook from public internet must require auth | | `FE-AI-018` | PASS | 0.6728649999986374ms | Credit balance drops by AI rate after personalised send (smoke) | | `FE-AI-019` | PASS | 0.2040419999993901ms | Toky + AI charges AI credits, not phone (smoke) | | `FE-AI-020` | PASS | 107.925475ms | cleanup-old-prompts endpoint exists at both legacy and /v1 paths | | `FE-AUTH-001` | PASS | 5301.722491ms | /login page renders with email + password + Forgot Password + Login + Register link | | `FE-AUTH-002` | PASS | 7561.841392000002ms | Login with valid credentials → redirect to /automation-campaign | | `FE-AUTH-003` | PASS | 338.70971700000155ms | Login with wrong password → friendly error, no stack trace | | `FE-AUTH-004` | PASS | 225.583208ms | Login with non-existent email → generic friendly error (no enumeration) | | `FE-AUTH-005` | PASS | 2036.1639190000024ms | Login with empty fields → form validation errors | | `FE-AUTH-006` | PASS | 1710.008901000001ms | Eye icon on password field toggles show/hide | | `FE-AUTH-007` | PASS | 3633.2848459999987ms | Forgot Password link → /forgot-password page renders | | `FE-AUTH-008` | PASS | 1160.2930699999997ms | Submit forgot password form with valid email → success message | | `FE-AUTH-009` | PASS | 318.45385499999975ms | Submit forgot password with non-existent email → generic success (anti-enumeration) | | `FE-AUTH-010` | PASS | 72.38493300000118ms | /auth-reset-password?token=invalid → friendly invalid token error | | `FE-AUTH-011` | PASS | 53.0826879999986ms | /auth-reset-password validates min length and match | | `FE-AUTH-012` | PASS | 38766.10208899999ms | Sign Out clears cookies and redirects to /login | | `FE-AUTH-013` | PASS | 6343.671617000007ms | Hard refresh of authenticated page → session restores, no console errors | | `FE-AUTH-014` | PASS | 2526.3039010000066ms | Direct nav to authenticated route while logged out → redirects to /login with ?redirect= | | `FE-AUTH-015` | PASS | 477.7507659999974ms | JWT expires mid-session → next API call triggers refresh; refresh fail → logout | | `FE-AUTH-016` | PASS | 3033.327820999999ms | Login on staging hits staging BFF, NOT prod (FE-BUG-002 regression) | | `FE-AUTH-017` | PASS | 4175.690519000011ms | BFF auth cookies have HttpOnly + Secure + SameSite flags | | `FE-AUTH-018` | PASS | 15705.083906ms | Open redirect — ?redirect=https://evil.com after login → blocked | | `FE-AUTH-019` | PASS | 1622.869510000004ms | Login button shows loading spinner while authenticating | | `FE-AUTH-020` | PASS | 803.2308489999996ms | Rate limiting on /login — rapid wrong-password attempts trigger 429 | | `FE-AUTH-021` | NOT_EXEC | 0ms | rapid wrong-password attempts on a unique email return 429 within budget | | `FE-AUTH-022` | NOT_EXEC | 0ms | 429 response carries a friendly message + retry hint, no internals leaked | | `FE-AUTH-023` | NOT_EXEC | 0ms | rate limit is scoped per-email (different fakes don't share budget) | | `FE-CAMP-001` | PASS | 11409.484102ms | /automation-campaign lists campaigns or shows empty state | | `FE-CAMP-002` | PASS | 6838.880787000002ms | Tabs Active / Inactive / All switch correctly | | `FE-CAMP-003` | PASS | 6572.730574000001ms | Search Campaigns input filters list | | `FE-CAMP-004` | PASS | 6962.351255000001ms | Pagination Next/Prev (where present) is sane | | `FE-CAMP-005` | PASS | 7459.208534999998ms | PULSE / SWARM tabs at top (SWARM dev-only on staging) | | `FE-CAMP-006` | PASS | 9296.267871999997ms | /automation-campaign/create renders builder | | `FE-CAMP-007` | PASS | 5688.7921489999935ms | Builder header shows 'Untitled' (FE-BUG-08 fix — not literal 'undefined') | | `FE-CAMP-008` | PASS | 12024.646359000006ms | Builder canvas shows 'Add Stage +' button | | `FE-CAMP-009` | PASS | 6915.138741999996ms | Click Add Stage opens stage type modal | | `FE-CAMP-010` | PASS | 7189.929072999992ms | Stage settings modal validates required fields (sanity check) | | `FE-CAMP-011` | PASS | 7746.745909000005ms | Builder name area exists (route smoke) | | `FE-CAMP-012` | PASS | 6907.8997970000055ms | Save Campaign with no stages → friendly error or warning | | `FE-CAMP-013` | PASS | 5622.725760999994ms | Active toggle persists after save (visual presence) | | `FE-CAMP-014` | PASS | 7583.046621000001ms | Click 'Import campaign from CRM' opens import modal | | `FE-CAMP-015` | PASS | 5.89093400001002ms | Import campaign validates CRM connection state (smoke) | | `FE-CAMP-016` | PASS | 7843.688082000008ms | /automation-campaign/[id] for non-existent ID → 404 or friendly error | | `FE-CAMP-017` | PASS | 6022.1455029999925ms | /automation-campaign/[id]/deals lists campaign deals | | `FE-CAMP-018` | PASS | 5219.898260999995ms | Deals table renders without crashing (smoke) | | `FE-CAMP-019` | PASS | 6193.242685000005ms | Pull CRM State button triggers sync (button present) | | `FE-CAMP-020` | PASS | 7652.593278000015ms | Stage drag-and-drop reorders without crashes (page renders) | | `FE-CAMP-021` | PASS | 7584.831472ms | Deals search page renders with filter components | | `FE-CAMP-022` | PASS | 4447.910961000001ms | Search filter updates URL params when typed + submitted | | `FE-CAMP-023` | PASS | 4377.681031ms | Status filter (when present) updates URL state | | `FE-CAMP-024` | PASS | 3738.5284600000014ms | Pagination controls (when present) navigate without crash | | `FE-CRED-001` | PASS | 4625.791221ms | Header credit balance widget reflects /v1/credit | | `FE-CRED-002` | PASS | 1.0151940000005197ms | Top-up via card increases balance (smoke @paid) | | `FE-CRED-003` | PASS | 1.2982220000003508ms | Twilio SMS decrements balance by 1 (smoke) | | `FE-CRED-004` | PASS | 0.6193649999995614ms | Twilio failure releases reserved credits (smoke) | | `FE-CRED-005` | PASS | 0.19957300000078249ms | Toky SMS leaves balance unchanged (smoke) | | `FE-CRED-006` | PASS | 0.9354269999994358ms | Personalised stage send charges AI unit not phone (smoke) | | `FE-CRED-007` | PASS | 0.26725900000019465ms | Failed AI personalization → AI credit not committed (smoke) | | `FE-CRED-008` | PASS | 0.18033600000035221ms | Hit balance=0 → friendly out-of-credits modal (smoke) | | `FE-CRED-009` | PASS | 2.8720400000001973ms | Add-on activation increases credit allowance (smoke) | | `FE-CRED-010` | PASS | 0.7321860000001834ms | Cancel add-on at end of period → next month reverts (smoke) | | `FE-CRM-001` | PASS | 4508.136585ms | Pipedrive verify-api-key endpoint exists | | `FE-CRM-002` | PASS | 0.5296580000003814ms | Invalid Pipedrive API key → friendly error (smoke) | | `FE-CRM-003` | PASS | 0.2756939999999304ms | Map Pipedrive user to RevHero account (smoke) | | `FE-CRM-004` | PASS | 0.2769980000002761ms | Enable CRM sync on campaign pulls existing deals (smoke) | | `FE-CRM-005` | PASS | 0.25485500000013417ms | Force-pull from CRM enqueues sweeper candidates (smoke) | | `FE-CRM-006` | PASS | 0.2800420000003214ms | Disable CRM sync stops auto-pulls (smoke) | | `FE-CRM-007` | PASS | 0.22771499999998923ms | Stage move with CRM connected → Pipedrive note + stage update (smoke) | | `FE-CRM-008` | PASS | 0.2662959999997838ms | Pipedrive webhook updates RevHero deal record (smoke) | | `FE-CRM-009` | PASS | 0.3334629999999379ms | Pipedrive outage → CRM goroutine fails silently, move proceeds (smoke) | | `FE-CRM-010` | PASS | 4391.1632039999995ms | HubSpot verify-api-key endpoint exists | | `FE-CROSS-001` | PASS | 8074.556178999999ms | All staging API calls go to *.test.revhero.io (FE-BUG-002 regression) | | `FE-CROSS-002` | PASS | 0.41246899999896414ms | Cloud-document uploads go to staging cloud-documents (smoke) | | `FE-CROSS-003` | PASS | 7210.900324000002ms | Dev-only routes accessible on staging (proxy.ts hostname check) | | `FE-CROSS-004` | PASS | 0.7084820000018226ms | Same routes redirect to /automation-campaign on prod (skip on staging) | | `FE-CROSS-005` | PASS | 0.23989699999947334ms | Free Plan signup completes without payment form (FE-BUG-001 regression) | | `FE-CROSS-006` | PASS | 0.24682100000063656ms | Plan-feature labels render numbers with thousands separators (smoke) | | `FE-CROSS-007` | PASS | 0.35108600000239676ms | Settings right-rail items don't truncate (smoke) | | `FE-CROSS-008` | PASS | 0.28631400000449503ms | Currency values show 2 decimals consistently (smoke) | | `FE-CROSS-009` | PASS | 0.2473119999995106ms | Campaign create header shows 'Untitled campaign' (FE-BUG-08 regression) | | `FE-CROSS-010` | PASS | 0.2512090000018361ms | Phone /sms doesn't get stuck on skeleton (FE-BUG-04 regression) | | `FE-CSV-001` | PASS | 4908.81128ms | GET /v1/csv-imports/template returns CSV template | | `FE-CSV-002` | PASS | 0.6121519999996963ms | POST /v1/stages/:id/csv-imports creates pending job (smoke) | | `FE-CSV-003` | PASS | 0.33534599999984493ms | Job advances pending → processing → completed (smoke) | | `FE-CSV-004` | PASS | 0.2774669999998878ms | Missing required columns → per-row error report (smoke) | | `FE-CSV-005` | PASS | 0.23303499999929045ms | Malformed phones skipped, valid rows ingested (smoke) | | `FE-CSV-006` | PASS | 0.317482999999811ms | Duplicate emails skipped or merged (smoke) | | `FE-CSV-007` | PASS | 3.1329369999994015ms | Reprocess endpoint idempotent (smoke) | | `FE-CSV-008` | PASS | 0.23121200000059616ms | GET /v1/stages/:id/csv-imports lists jobs (smoke) | | `FE-CSV-009` | PASS | 0.3847279999999955ms | suppress-import-actions flag toggles trigger behavior (smoke) | | `FE-CSV-010` | PASS | 0.5215729999999894ms | Oversized CSV handled gracefully (smoke) | | `FE-CSV-011` | PASS | 85.06748399999992ms | lead_ingestion_analytics table exists in deals-actions DB | | `FE-CSV-012` | PASS | 0.6173229999999421ms | lead-ingestion event contract has v1 (smoke) | | `FE-CSV-013` | PASS | 0.35418000000004213ms | OAuth2 token acquisition + refresh via leadingestion/auth.go (smoke) | | `FE-CSV-014` | PASS | 0.3296860000000379ms | analytics record carries success / partial / failed status (smoke) | | `FE-DEAL-001` | PASS | 785.7254960000002ms | Sweep endpoint returns jobs_scheduled count | | `FE-DEAL-002` | PASS | 0.47595799999999144ms | Sweep moves a deal forward (smoke — needs seeded campaign) | | `FE-DEAL-003` | PASS | 0.33699899999965055ms | Inactive campaign skips sweep (smoke) | | `FE-DEAL-004` | PASS | 0.3463169999999991ms | Inactive user's stages skipped (smoke) | | `FE-DEAL-005` | PASS | 0.21508100000028207ms | max_deals_to_move quota respected (smoke) | | `FE-DEAL-006` | PASS | 0.3325110000000677ms | Worker pops job within 5s (smoke) | | `FE-DEAL-007` | PASS | 0.23581999999987602ms | Worker triggers email/SMS action (smoke) | | `FE-DEAL-008` | PASS | 0.16446700000005876ms | Action returns 425 → deal rolled back (smoke) | | `FE-DEAL-009` | PASS | 3.7837719999997717ms | Action 5xx → exponential backoff retry (smoke) | | `FE-DEAL-010` | PASS | 0.39073899999993955ms | Successful move appears in BFF /v1/deals/filter (smoke) | | `FE-DEAL-011` | PASS | 0.23260399999981018ms | Successful move does NOT create activity row (smoke) | | `FE-DEAL-012` | PASS | 0.17506699999967168ms | CRM sync goroutine fires after move (smoke) | | `FE-DEAL-013` | PASS | 293.6842230000002ms | /v1/stages/scheduled endpoint reachable | | `FE-DEAL-014` | PASS | 162.47094500000003ms | /v1/stages/moved endpoint reachable | | `FE-DEAL-015` | PASS | 0.3304670000002261ms | Redis isolation between staging and prod (smoke) | | `FE-DEAL-016` | PASS | 3.6099380000000565ms | Super-admin sweeper button (smoke) | | `FE-DEAL-017` | PASS | 7.2300930000001244ms | next_move_date NULL → deal NOT picked up (smoke) | | `FE-DEAL-SEARCH-001` | PASS | 7315.793584999999ms | Type email into Search Deals input → table filters by email | | `FE-DEAL-SEARCH-002` | PASS | 4141.680276000001ms | Type partial first name → filter applies | | `FE-DEAL-SEARCH-003` | PASS | 3646.252623999999ms | Press Enter in search submits without page reload | | `FE-DEAL-SEARCH-004` | PASS | 4694.851015ms | Clear input → full deal list returns | | `FE-DEAL-SEARCH-005` | PASS | 0.5670969999991939ms | Search persists across pagination | | `FE-DEAL-SEARCH-006` | PASS | 4984.987557ms | Include lost deals toggle ON/OFF persists | | `FE-E2E-001` | PASS | 7.148370999999997ms | Signup → Onboarding → First campaign → First deal email sent (smoke) | | `FE-E2E-002` | PASS | 2.627473000000009ms | Inbound reply NEGATIVE → deal auto-Lost (smoke) | | `FE-E2E-003` | PASS | 0.5179359999999633ms | Toky BYOC end-to-end SMS round-trip (smoke) | | `FE-E2E-004` | PASS | 0.3367880000000696ms | Multi-stage campaign with wait + actions (smoke) | | `FE-E2E-005` | PASS | 0.41281099999991966ms | Quota cap respected (smoke) | | `FE-E2E-006` | PASS | 0.5303199999998469ms | Inactive user pause-billing edge case (smoke) | | `FE-E2E-007` | PASS | 0.40533700000014505ms | Free-plan SMS via Toky succeeds, via Twilio rejected (smoke) | | `FE-E2E-008` | PASS | 0.47143000000005486ms | JWT expiry mid-flow auto-refreshes (smoke) | | `FE-E2E-009` | PASS | 1.1056630000000496ms | Multi-tenant isolation across mailboxes/Toky/deals (smoke) | | `FE-E2E-010` | PASS | 0.5389359999999215ms | Webhook-only services not exposed (smoke — covered by WEBHOOK-AUTH) | | `FE-EMAIL-001` | PASS | 5677.386491ms | /email-system/email lists messages or empty state | | `FE-EMAIL-002` | PASS | 4345.021178ms | 'Mailbox not connected' empty state with Connect Mailbox CTA | | `FE-EMAIL-003` | PASS | 6674.996792ms | Connect Mailbox button opens OAuth flow (button presence) | | `FE-EMAIL-004` | PASS | 6954.993644999999ms | Email filters work (filter UI exists) | | `FE-EMAIL-005` | PASS | 5111.114448ms | Search Emails input filters list | | `FE-EMAIL-006` | PASS | 5922.011981000003ms | '+' button opens compose / add modal | | `FE-EMAIL-007` | PASS | 5898.904458999998ms | /email-system/email/add page renders without crashing | | `FE-EMAIL-008` | PASS | 5339.757360999996ms | Compose form requires recipient + subject + body | | `FE-EMAIL-009` | PASS | 6.683744999994815ms | Send email triggers BFF call (network observation) | | `FE-EMAIL-010` | PASS | 5428.009603000006ms | /email-system/email/[id] shows email detail (route accessible) | | `FE-EMAIL-011` | PASS | 6514.737097999998ms | Email categories sidebar navigates | | `FE-EMAIL-012` | PASS | 9.020495999997365ms | Pagination at top right (X of Y) updates with results | | `FE-EMAIL-IN-001` | PASS | 3.5120139999999083ms | Reply appears in FE thread within ~30s (smoke) | | `FE-EMAIL-IN-002` | PASS | 0.5062540000001263ms | email row has sentiment after scoring (smoke) | | `FE-EMAIL-IN-003` | PASS | 0.3658020000000306ms | 'unsubscribe' triggers NEGATIVE heuristic + deal-loss (smoke) | | `FE-EMAIL-IN-004` | PASS | 1.4196099999999205ms | OOO autoresponder → NEUTRAL + date extraction (smoke) | | `FE-EMAIL-IN-005` | PASS | 1.908051000000114ms | Self-reply skips sentiment (smoke) | | `FE-EMAIL-IN-006` | PASS | 0.6186659999998483ms | Inbound XSS subject escapes on render (smoke) | | `FE-EMAIL-IN-007` | PASS | 0.5612169999999423ms | Bounce notification flags original send (smoke) | | `FE-EMAIL-IN-008` | PASS | 267.7796460000002ms | POST /v1/emails/sentiment-webhook from public internet — must return 401/403/404 | | `FE-EMAIL-IN-009` | PASS | 8.03103800000008ms | Mark inbound email Read/Unread/Favourite/Archive (smoke) | | `FE-EMAIL-IN-010` | PASS | 8.111850000000231ms | Conversation history merges sent + received chronologically (smoke) | | `FE-EMAIL-IN-011` | PASS | 0.32217199999968216ms | Cross-tenant email isolation (smoke) | | `FE-EMAIL-IN-012` | PASS | 261.021444ms | bounce-webhook endpoint requires internal-services auth | | `FE-EMAIL-IN-013` | PASS | 127.99280700000008ms | duplicate bounces within debounce window are absorbed (smoke) | | `FE-EMAIL-IN-014` | PASS | 1.9838519999998425ms | bounced address propagates to the blocklist (smoke) | | `FE-EMAIL-OUT-001` | PASS | 5288.822501000001ms | Connect Gmail mailbox via OAuth (external-blocked) — assert URL request returns redirec... | | `FE-EMAIL-OUT-002` | PASS | 3647.9980829999995ms | Connect Microsoft mailbox same flow (external-blocked) | | `FE-EMAIL-OUT-003` | PASS | 17.722185999999056ms | Disconnect mailbox via POST /v1/user-mailboxes/:id/disconnect (smoke) | | `FE-EMAIL-OUT-004` | PASS | 4421.1106070000005ms | Test Email (rate-limited 5/hr) endpoint exists | | `FE-EMAIL-OUT-005` | PASS | 5.592697000000044ms | Test-Email button disabled when ai_personalization_enabled=false (FE check) | | `FE-EMAIL-OUT-006` | PASS | 6.188829999999143ms | 6 test emails in 1 hour → 429 | | `FE-EMAIL-OUT-007` | PASS | 5768.914189000001ms | Send manual email with merge tags (composer renders) | | `FE-EMAIL-OUT-008` | PASS | 0.38717300000280375ms | Manual email to deal contact creates sent_email row (smoke) | | `FE-EMAIL-OUT-009` | PASS | 0.3236739999993006ms | Email signature appended to outbound (smoke) | | `FE-EMAIL-OUT-010` | PASS | 5096.701278999997ms | Empty subject → form validation rejects | | `FE-EMAIL-OUT-011` | PASS | 0.6802999999999884ms | Long body (>50 KB) handled (smoke — no 500) | | `FE-EMAIL-OUT-012` | PASS | 0.267851000000519ms | XSS in subject + body sanitised (smoke — no script execution) | | `FE-EMAIL-OUT-013` | PASS | 1447.2227090000001ms | POST /v1/templates/render endpoint exists on email-ingress | | `FE-EMAIL-OUT-014` | PASS | 59.64092899999969ms | invalid deal_id is rejected (no panic) | | `FE-EMAIL-OUT-015` | PASS | 68.60461900000018ms | missing required field returns 400 | | `FE-HELP-001` | PASS | 4892.586175ms | /help renders FAQ section (dev-only on staging) | | `FE-HELP-002` | PASS | 0.4639660000011645ms | Search articles input filters FAQ | | `FE-HELP-003` | PASS | 1.2090969999990193ms | Help category cards render | | `FE-HELP-004` | PASS | 2.5461320000013075ms | Contact Support button opens email/chat | | `FE-HELP-005` | PASS | 4799.440224ms | /getting-started checklist progress bar updates | | `FE-HELP-006` | PASS | 3.8080769999996846ms | Watch Video opens modal | | `FE-HELP-007` | PASS | 0.21450000000004366ms | Add Signature redirects to email signature settings | | `FE-HELP-008` | PASS | 0.273339000001215ms | Start Guided Walkthrough triggers tour | | `FE-LAY-001` | PASS | 6847.337497ms | Sidebar visible on every authenticated dashboard page | | `FE-LAY-002` | PASS | 5866.5050360000005ms | Sidebar shows Dashboard / Campaign / Phone / Email / Settings (smoke) | | `FE-LAY-003` | PASS | 4863.988641999998ms | Header shows credit balance, notification bell, sign out | | `FE-LAY-004` | PASS | 0.46684000000095693ms | Active sidebar item highlighted in primary color (smoke) | | `FE-LAY-005` | PASS | 0.26348200000211364ms | Click sidebar item navigates without full page reload (smoke) | | `FE-LAY-006` | PASS | 2.4968189999999595ms | Mobile nav (<768px) collapses sidebar to hamburger (smoke) | | `FE-LAY-007` | PASS | 0.3498230000004696ms | Layout no CLS when notifications appear/dismiss (smoke) | | `FE-LAY-008` | PASS | 3.9970200000025216ms | Layout no CLS when modals open/close (smoke) | | `FE-MISC-001` | PASS | 0.13817800000106217ms | Wrong current password → friendly error not 500 (smoke) | | `FE-MISC-002` | PASS | 0.2014259999996284ms | Same-as-old password blocked (smoke) | | `FE-MISC-003` | PASS | 0.14510100000188686ms | Password change success → next login uses new (smoke) | | `FE-MISC-004` | PASS | 0.8251499999969383ms | Driver License upload accepts JPG/PNG/PDF (smoke) | | `FE-MISC-005` | PASS | 0.6027840000024298ms | Driver License rejects >5 MB (smoke) | | `FE-MISC-006` | PASS | 6717.303328999995ms | GET /v1/user/driver-license returns previously uploaded | | `FE-MISC-007` | PASS | 0.8371920000063255ms | Add invalid card → friendly inline error (smoke) | | `FE-MISC-008` | PASS | 0.18395299999974668ms | Delete only payment method blocked (smoke) | | `FE-MISC-009` | PASS | 0.15007999999943422ms | Set default payment method updates (smoke) | | `FE-MISC-010` | PASS | 0.12269899999955669ms | AI Customized Templates editor saves + renders (smoke) | | `FE-NOTIF-001` | PASS | 5118.122149ms | /notifications renders list with All / Read / Unread tabs | | `FE-NOTIF-002` | PASS | 0.5864739999997255ms | Tabs filter notifications | | `FE-NOTIF-003` | PASS | 0.34109600000010687ms | Sub-tabs Email/SMS/LinkedIn/System filter by type | | `FE-NOTIF-004` | PASS | 0.3086760000005597ms | Mark all as read clears unread | | `FE-NOTIF-005` | PASS | 4.172187000000122ms | Per-row 'Mark as Read' updates row (smoke) | | `FE-NOTIF-006` | PASS | 2.543046000000686ms | Notifications scoped to current account_id (smoke) | | `FE-NOTIF-007` | PASS | 0.3145470000008572ms | Bell icon shows unread badge (smoke) | | `FE-NOTIF-008` | PASS | 0.2549749999998312ms | Relative timestamps update (smoke) | | `FE-PERF-001` | PASS | 6214.937586ms | /automation-campaign reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-002` | PASS | 5943.884435000001ms | /dashboard reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-003` | PASS | 4566.131356000002ms | /email-system/email reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-004` | PASS | 5628.938611000001ms | /phone-system/sms reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-005` | PASS | 5570.653147999998ms | /notifications reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-006` | PASS | 8169.135614999999ms | /settings/general reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-007` | PASS | 8861.385818999996ms | /admin/dashboard reaches DOMContentLoaded under 15000ms (warn >5000ms) | | `FE-PERF-008` | PASS | 10408.309573999999ms | axe-core /automation-campaign zero serious violations | | `FE-PERF-009` | PASS | 8226.448775999997ms | axe-core /settings/general zero serious violations | | `FE-PERF-010` | PASS | 6353.629925000008ms | Form inputs have associated labels (axe label rule) | | `FE-PERF-011` | PASS | 9201.586190000002ms | Color contrast meets WCAG AA (axe color-contrast rule) | | `FE-PERF-012` | PASS | 1.1460990000050515ms | Tab order through forms is logical (smoke) | | `FE-PERF-013` | PASS | 5703.486755999998ms | Images have alt text (axe image-alt rule) | | `FE-PERF-014` | PASS | 0.45664299999771174ms | Page titles match content (smoke) | | `FE-PERF-015` | PASS | 4481.670975000001ms | No 404s on static assets (smoke — checked via network observer) | | `FE-PHONE-001` | PASS | 4829.814059ms | /phone-system redirects to /phone-system/sms | | `FE-PHONE-002` | PASS | 6300.328173ms | /phone-system/sms renders empty state (FE-BUG-04 fix — no permanent skeleton) | | `FE-PHONE-003` | PASS | 3903.1529310000024ms | Empty state shows 'Go to Phone System Settings' CTA (best-effort) | | `FE-PHONE-004` | PASS | 4571.860063ms | /phone-system/voicemails lists voicemails or empty state | | `FE-PHONE-005` | PASS | 2.3979249999974854ms | Brand registration flow accessible from phone system | | `FE-PHONE-006` | PASS | 0.4501299999974435ms | A2P status badge displays correct color/text (smoke) | | `FE-PHONE-007` | PASS | 0.8322640000005777ms | Phone Number purchase flow renders available numbers (smoke) | | `FE-PHONE-008` | PASS | 0.3445030000002589ms | Buy phone number button triggers purchase API call (smoke) | | `FE-PHONE-009` | PASS | 0.6823340000009921ms | Send SMS modal validates recipient + body (smoke) | | `FE-PUR-001` | PASS | 7189.203220999996ms | /purchase-lists renders (dev-only on staging) | | `FE-PUR-002` | PASS | 0.46574999999575084ms | Search by Name filters list (smoke) | | `FE-PUR-003` | PASS | 0.18243100000108825ms | Status badges In Progress / Success render correct colors (smoke) | | `FE-PUR-004` | PASS | 0.7205549999998766ms | Download Leads icon enabled only on Success (smoke) | | `FE-PUR-005` | PASS | 6524.673994000004ms | /purchase-lists/create renders form | | `FE-PUR-006` | PASS | 0.41335099999560043ms | Create requires name + filters (smoke) | | `FE-PUR-007` | PASS | 0.20391099999687867ms | Pagination Rows-per-page selector (smoke) | | `FE-PUR-008` | PASS | 0.7312450000026729ms | Targeting eye-icon opens detail modal (smoke) | | `FE-REG-001` | PASS | 5378.719311000001ms | /signup step 1 renders register form (name, email, password, phone) | | `FE-REG-002` | PASS | 6696.5036629999995ms | Submit empty form → field validation errors | | `FE-REG-003` | PASS | 2982.453770000002ms | Submit with invalid email format → validation error | | `FE-REG-004` | PASS | 3170.6960999999974ms | Submit with weak password → strength meter + rejection | | `FE-REG-005` | PASS | 1448.1162580000018ms | Submit with duplicate email → friendly 'email already registered' | | `FE-REG-006` | PASS | 24452.610802ms | Submit valid form → advances to step 2 (Select Plan) | | `FE-REG-007` | PASS | 2293.1209890000027ms | Step 2 shows Pulse Free + Pulse Pro side-by-side | | `FE-REG-008` | PASS | 1560.2262460000056ms | Toggle Monthly / Annually switches plan prices | | `FE-REG-009` | PASS | 2434.252336000005ms | Click Free Plan → advances to step 4 (Order Information), skipping step 3 | | `FE-REG-010` | PASS | 4666.778632000001ms | Click Pulse Pro → advances to step 3 (Add-ons) | | `FE-REG-011` | PASS | 1333.4895449999967ms | Step 4 (Free Plan) — Total Due $0.00, 'Continue' button (NOT 'Continue to Payment') | | `FE-REG-012` | PASS | 3525.9167050000033ms | Step 4 (Free Plan) — promo code field hidden (FE-BUG-001 fix) | | `FE-REG-013` | PASS | 1314.7615440000009ms | Step 4 (Free Plan) — clicking Continue skips payment form | | `FE-REG-014` | NOT_EXEC | 0ms | Step 4 (Paid plan) — Payment Information form shows card fields @paid | | `FE-REG-015` | NOT_EXEC | 0ms | Step 4 — apply promo code BETAOFFER → 100% discount → label changes @paid | | `FE-REG-016` | NOT_EXEC | 0ms | Step 4 — invalid promo code → friendly error @paid | | `FE-REG-017` | NOT_EXEC | 0ms | Step 4 — expired promo code → friendly error @paid | | `FE-REG-018` | NOT_EXEC | 0ms | Step 4 — payment form rejects invalid card number @paid | | `FE-REG-019` | NOT_EXEC | 0ms | Step 4 — payment form rejects expired card @paid | | `FE-REG-020` | NOT_EXEC | 0ms | Step 4 — payment form requires all billing address fields @paid | | `FE-REG-021` | PASS | 4861.298875000008ms | Browser back button mid-wizard preserves form state | | `FE-REG-022` | PASS | 4898.321465000001ms | Direct nav to ?step=4 without completing 1-3 → redirects | | `FE-REG-023` | PASS | 7001.318956000003ms | Already-authenticated user navigating to /signup → redirects to dashboard | | `FE-REG-024` | PASS | 1189.5577780000021ms | Wizard step indicator updates correctly per step | | `FE-ROLE-001` | PASS | 3720.6478370000004ms | MEMBER login lands on dashboard | | `FE-ROLE-002` | PASS | 10671.146265ms | MEMBER /admin/dashboard redirected or 403 | | `FE-ROLE-003` | PASS | 0.8182880000022124ms | MEMBER PUT /v1/admin/plans/:id → 403 (smoke) | | `FE-ROLE-004` | PASS | 0.22907699999996112ms | MEMBER scoped to own account_id (smoke) | | `FE-ROLE-005` | PASS | 0.26598700000249664ms | MEMBER admin-only actions hidden from UI (smoke) | | `FE-ROLE-006` | PASS | 0.752543999999034ms | MEMBER edits own profile, can't delete account (smoke) | | `FE-SEAT-001` | PASS | 10365.642844ms | Sub-User Invite form requires valid email + role | | `FE-SEAT-002` | PASS | 1.1090599999988626ms | Invite creates pending seat row (smoke) | | `FE-SEAT-003` | PASS | 0.24742200000036974ms | Recipient receives /invite/[id] email (smoke) | | `FE-SEAT-004` | PASS | 19251.408478ms | /invite/[id] shows account + inviter + Accept | | `FE-SEAT-005` | PASS | 0.5522310000014841ms | Accept invite from new email creates account (smoke) | | `FE-SEAT-006` | PASS | 2.03030900000158ms | Reuse accepted invite shows already-accepted (smoke) | | `FE-SEAT-007` | PASS | 0.29028200000175275ms | Expired invite shows expired-link (smoke) | | `FE-SEAT-008` | PASS | 0.203349999996135ms | Admin revokes seat → MEMBER session invalidated (smoke) | | `FE-SEAT-009` | PASS | 5256.503481ms | GET /v1/user/seats returns seat count | | `FE-SEC-001` | PASS | 500.6741609999999ms | Direct API call to BFF without token → 401 | | `FE-SEC-002` | PASS | 24.392795000000206ms | Manipulated JWT → 401, FE redirects to login | | `FE-SEC-003` | PASS | 3.698992999999973ms | XSS via campaign name escaped on render (smoke) | | `FE-SEC-004` | PASS | 0.31798299999991286ms | XSS via email signature escaped (smoke) | | `FE-SEC-005` | PASS | 0.5739209999997001ms | XSS via business profile description escaped (smoke) | | `FE-SEC-006` | PASS | 0.3552829999998721ms | SQL injection via search inputs safe (smoke — Prisma parameterised) | | `FE-SEC-007` | PASS | 5944.630906999999ms | Direct nav to /admin/* as non-admin blocked | | `FE-SEC-008` | PASS | 0.3122429999984888ms | IDOR /automation-campaign/[id] foreign id → 403/404 (smoke) | | `FE-SEC-009` | PASS | 1.8254679999990913ms | IDOR /admin/billing/clients/[id] foreign id → 403/404 (smoke) | | `FE-SEC-010` | PASS | 6.323250000001281ms | File upload wrong type rejected (smoke) | | `FE-SEC-011` | PASS | 5.095670000000609ms | File upload oversized rejected (smoke) | | `FE-SEC-012` | PASS | 1.2286330000006274ms | File upload uses staging cloud-documents URL (regression) | | `FE-SEC-013` | PASS | 1.9915369999998802ms | CSP blocks third-party scripts not in allowlist (smoke) | | `FE-SEC-014` | PASS | 1131.891255999999ms | Frames-ancestors blocks iframe embedding (smoke) | | `FE-SEC-015` | PASS | 0.5524000000004889ms | Logout invalidates session token (smoke) | | `FE-SET-G-001` | PASS | 11519.096287ms | /settings/general renders Billing section by default | | `FE-SET-G-002` | PASS | 14551.245748000001ms | Right sidebar nav items show full text (fe-ui-01 regression) | | `FE-SET-G-003` | PASS | 0.8010040000008303ms | Right sidebar nav items list (smoke) | | `FE-SET-G-004` | PASS | 0.6334720000013476ms | Switching nav items updates panel without reload (smoke) | | `FE-SET-G-005` | PASS | 0.25371399999858113ms | Current Plan card shows plan name + price (smoke) | | `FE-SET-G-006` | PASS | 6006.196733000001ms | Plan features show numbers with thousands separators (fe-ui-02 regression) | | `FE-SET-G-007` | PASS | 1.5115719999957946ms | Promo code card shows discount + expiry + COPY (smoke) | | `FE-SET-G-008` | PASS | 0.3122319999965839ms | Manage Add-ons button opens manage-plans page (smoke) | | `FE-SET-G-009` | PASS | 2.0791410000019823ms | CRM API tab shows connected CRM with masked key (smoke) | | `FE-SET-G-010` | PASS | 0.4729719999959343ms | Sub-User Management lists invited users (smoke) | | `FE-SET-G-011` | PASS | 0.2160130000047502ms | Sub-User Invite form validates email (smoke) | | `FE-SET-G-012` | PASS | 0.18236000000615604ms | Active Hours tab allows setting weekly schedule (smoke) | | `FE-SET-G-013` | PASS | 0.2046619999964605ms | Email Signatures tab renders editor + preview (smoke) | | `FE-SET-G-014` | PASS | 2.4709320000038133ms | Book Link tab persists URL (smoke) | | `FE-SET-G-015` | PASS | 0.2836390000011306ms | AI Customized Templates tab lists templates (smoke) | | `FE-SET-G-016` | PASS | 7151.050148ms | /settings/general AI Chat Response tab renders | | `FE-SET-G-017` | PASS | 5111.308973000001ms | Selecting a campaign reveals channel configuration toggles | | `FE-SET-G-018` | PASS | 4424.503438ms | AI chat config persists in LocalStorage across reload | | `FE-SET-M-001` | PASS | 8190.322895000005ms | /settings/manage-plans renders manage add-ons section | | `FE-SET-M-002` | PASS | 0.38018000000010943ms | Existing add-ons list (smoke) | | `FE-SET-M-003` | PASS | 0.141193999996176ms | Empty state friendly message (smoke) | | `FE-SET-M-004` | PASS | 0.12735799999791197ms | Available add-ons list (smoke) | | `FE-SET-M-005` | PASS | 0.1259250000002794ms | Add new add-on triggers checkout flow (smoke) | | `FE-SET-M-006` | PASS | 1.2500940000027185ms | Cancel add-on triggers confirmation modal (smoke) | | `FE-SET-S-001` | PASS | 7082.080406000001ms | /settings/system renders Brand Status + Phone Settings | | `FE-SET-S-002` | PASS | 3.349750999994285ms | Right sidebar Phone / Email Settings (smoke) | | `FE-SET-S-003` | PASS | 1.0137519999989308ms | Brand Status badge color matches status (smoke) | | `FE-SET-S-004` | PASS | 11.958659000003536ms | Register Brand button opens modal (smoke) | | `FE-SET-S-005` | PASS | 0.29831600000034086ms | Phone Number Settings shows empty state (smoke) | | `FE-SET-S-006` | PASS | 0.21301800000219373ms | Email Settings shows OAuth connection state (smoke) | | `FE-SET-S-007` | PASS | 0.2946589999992284ms | Save Route button persists route preferences (smoke) | | `FE-SETUP-001` | PASS | 8716.937515ms | /setup page renders for newly-signed-up users | | `FE-SETUP-002` | PASS | 1087.1024779999989ms | Setup wizard captures business profile info (form renders) | | `FE-SETUP-003` | PASS | 5849.133484ms | Skip onboarding via 'Skip for now' → dashboard with welcome modal (skip CTA exists) | | `FE-SETUP-004` | PASS | 5405.022519999999ms | /getting-started shows checklist with at least one step | | `FE-SETUP-005` | PASS | 6241.222618ms | Welcome greeting renders user's first name | | `FE-SETUP-006` | PASS | 219.73922500000117ms | setup_finished flag persists in DB for the test admin | | `FE-SETUP-007` | PASS | 5814.3985200000025ms | /getting-started accessible even when setup_finished is true (onboarding route exception) | | `FE-SIG-001` | PASS | 0.20313999999780208ms | Save email signature persists across reload (smoke) | | `FE-SIG-002` | PASS | 0.1540580000000773ms | Outbound email shows signature appended (smoke) | | `FE-SIG-003` | PASS | 0.23186300000088522ms | Save booking link persists (smoke) | | `FE-SIG-004` | PASS | 0.1468040000036126ms | {{sender_booking_link}} merge tag rendered (smoke) | | `FE-SIG-005` | PASS | 0.13204599999880884ms | Empty signature → no trailing artifact (smoke) | | `FE-SMS-TOKY-001` | PASS | 2.2721010000000206ms | BYOC card lists Toky in /settings/system (smoke) | | `FE-SMS-TOKY-002` | PASS | 1.1479719999999816ms | Save bogus Toky API key → red error (smoke) | | `FE-SMS-TOKY-003` | PASS | 0.3197059999999965ms | Save real test API key creates carrier_credentials row (smoke) | | `FE-SMS-TOKY-004` | PASS | 0.3035970000000816ms | Save handshake registers webhook on Toky side (smoke) | | `FE-SMS-TOKY-005` | PASS | 0.2764959999999519ms | Re-save credential clears existing webhook auth (smoke) | | `FE-SMS-TOKY-006` | PASS | 0.3547719999999117ms | Add Toky Number lists user inventory (smoke) | | `FE-SMS-TOKY-007` | PASS | 0.24876300000005358ms | Import test number creates phonenumbers row (smoke) | | `FE-SMS-TOKY-008` | PASS | 0.21350900000015827ms | Send SMS via Toky doesn't decrement credits (smoke) | | `FE-SMS-TOKY-009` | PASS | 0.7554690000001756ms | Reply lands inbound within 10s (smoke) | | `FE-SMS-TOKY-010` | PASS | 876.3390190000002ms | Toky inbound webhook validates HTTP Basic auth | | `FE-SMS-TOKY-011` | PASS | 0.38210300000037023ms | Toky payload is JSON array (smoke) | | `FE-SMS-TOKY-012` | PASS | 0.22437900000022637ms | Toky inbound NEGATIVE sentiment marks deal LOST (smoke) | | `FE-SMS-TOKY-013` | PASS | 0.22124299999995856ms | Replay same payload twice → idempotent (smoke) | | `FE-SMS-TOKY-014` | PASS | 3.3626549999999042ms | Disconnect Toky credential revokes webhook (smoke) | | `FE-SMS-TOKY-015` | PASS | 0.6243249999997715ms | Twilio + Toky number coexistence (smoke) | | `FE-SMS-TOKY-016` | PASS | 0.3475090000001728ms | AI personalization charges AI credits but not phone (smoke) | | `FE-SMS-TOKY-017` | PASS | 1.8950559999998404ms | Twilio regression after Toky enabled (smoke) | | `FE-SMS-TOKY-018` | PASS | 0.4668499999997948ms | Legacy provider=NULL treated as 'twilio' (smoke) | | `FE-SMS-TOKY-019` | PASS | 0.14474999999993088ms | Toky API key with zero numbers → empty inventory (smoke) | | `FE-SMS-TOKY-020` | PASS | 0.21884899999986374ms | Webhook dedup respects DRF pagination shape (smoke) | | `FE-SMS-TW-001` | PASS | 2.8196629999999914ms | A2P brand registration starts from /settings/system (smoke) | | `FE-SMS-TW-002` | PASS | 0.37550099999998565ms | Phone-number purchase blocked when A2P != brand_approved | | `FE-SMS-TW-003` | PASS | 0.28302800000005846ms | Phone-number purchase succeeds when approved | | `FE-SMS-TW-004` | PASS | 0.4786540000000059ms | Send SMS from /phone-system/sms (smoke) | | `FE-SMS-TW-005` | PASS | 0.2983870000000479ms | Send SMS with merge tag rendered correctly | | `FE-SMS-TW-006` | PASS | 0.8547869999999875ms | Send SMS with no credits → 402 / out-of-credits | | `FE-SMS-TW-007` | PASS | 0.30011100000001534ms | Twilio API failure releases reserved credits | | `FE-SMS-TW-008` | PASS | 0.24839399999996203ms | Send SMS exceeds rate limit → 429 | | `FE-SMS-TW-009` | PASS | 0.25467600000001767ms | Inbound SMS appears in FE thread (smoke) | | `FE-SMS-TW-010` | PASS | 1.0848960000000716ms | Inbound negative sentiment marks deal LOST (smoke) | | `FE-SMS-TW-011` | PASS | 208.47960300000022ms | Twilio webhook with wrong bears_key → 401 | | `FE-SMS-TW-012` | PASS | 0.3221909999997479ms | Twilio status callback updates messages.status (smoke) | | `FE-USER-001` | PASS | 7893.331063000005ms | /user renders profile fields | | `FE-USER-002` | PASS | 6343.839425999999ms | Created date renders correctly (NOT 'Invalid Date') | | `FE-USER-003` | PASS | 0.6886050000030082ms | Change Password button opens modal (smoke) | | `FE-USER-004` | PASS | 0.28742699999565957ms | Password modal validates fields (smoke) | | `FE-USER-005` | PASS | 16.773605000002135ms | Wrong current password → friendly error (smoke) | | `FE-USER-006` | PASS | 0.678196000000753ms | Password change success closes modal + toast (smoke) | | `FE-VM-001` | PASS | 3111.7630959999997ms | /phone-system/voicemails lists records or empty state | | `FE-VM-002` | PASS | 0.583560000000034ms | Each row shows recipient/duration/status/timestamp (smoke) | | `FE-VM-003` | PASS | 0.2605059999996229ms | Play affordance plays audio inline (smoke) | | `FE-VM-004` | PASS | 0.2084789999998975ms | Filter by status works (smoke) | | `FE-VM-005` | PASS | 0.2499260000004142ms | Pagination works at boundaries (smoke) | | `FE-VM-006` | PASS | 0.2290869999997085ms | Voicemail audio URL is signed/scoped (smoke) | | `JUDY-001` | PASS | 556.4053879999997ms | Unauth GET /v1/judy/conversations rejected | | `JUDY-002` | PASS | 4540.654055ms | Authed GET /v1/judy/conversations does not 5xx (service up) | | `JUDY-003` | PASS | 299.3917650000003ms | Bogus sibling path 404s distinctly from the real route's 401 (route-exists sanity) | | `OPT-001` | PASS | 460.92723399999977ms | Unauth GET /v1/optimization/signoffs rejected | | `OPT-002` | PASS | 5030.711450999999ms | Authed GET /v1/optimization/signoffs does not 5xx (service up) | | `OPT-003` | PASS | 93.26971199999934ms | Bogus sibling path 404s distinctly from the real route's 401 (route-exists sanity) | | `UNKNOWN-4jjmgy` | PASS | 1564.245867ms | WITH the lock, exactly one process logs in per role | | `UNKNOWN-6rxc7t` | PASS | 0.3545019999999113ms | false for malformed state | | `UNKNOWN-aiji3u` | PASS | 4.6751050000000305ms | warms every role, serially and in order, when all succeed | | `UNKNOWN-b9y7lb` | PASS | 1.77226799999994ms | falls back to exponential backoff with jitter when no Retry-After is given | | `UNKNOWN-gh9hry` | PASS | 1.0837930000000142ms | respects QA_LOGIN_PREWARM_BUDGET_MS when no explicit budgetMs is given | | `UNKNOWN-ifgt2d` | PASS | 5.803812999999991ms | honors Retry-After across repeated 429s, then succeeds | | `UNKNOWN-kkwrk6` | PASS | 0.22504000000003543ms | false when there is no token cookie | | `UNKNOWN-le5tlh` | PASS | 0.9193759999998292ms | skips the login entirely when a fresh cached session already exists | | `UNKNOWN-mbhl92` | PASS | 2238.45714ms | WITHOUT the lock, concurrent processes each log in independently (>1) | | `UNKNOWN-mmwghr` | PASS | 0.27555500000016764ms | false when the token cookie is already expired | | `UNKNOWN-oxwob1` | PASS | 4.314021000000025ms | keeps going after an early role fails (e.g. 429 budget exhausted) | | `UNKNOWN-p9ogdl` | PASS | 0.6454650000000584ms | does NOT skip when the cached session exists but isFresh says no | | `UNKNOWN-prctny` | PASS | 0.8399380000000747ms | true when the token cookie has well over the required margin left | | `UNKNOWN-s91huy` | PASS | 3.0691289999999753ms | fails with a clear role + rate-limit error after the budget is exhausted | | `UNKNOWN-y6m9ep` | PASS | 0.24230199999988145ms | false when the token cookie expires inside the required margin | | `UNKNOWN-yqzcb1` | PASS | 3.1979989999999816ms | skips a role with no configured credentials instead of aborting the suite (2026-10-02 r... | | `WEBHOOK-AUTH-001` | PASS | 426.3774229999999ms | email-ingress sentiment-webhook rejects without auth | | `WEBHOOK-AUTH-002` | PASS | 444.770029ms | sms-service sentiment-webhook rejects without auth | | `WEBHOOK-AUTH-003` | PASS | 858.867324ms | deal-mover sweeper rejects without auth |