Inbox Unified Timeline — Production Review

nexus.rodeoleads.com/inbox · live prod · 2026-06-03 · read-only verification + population-wide DB audit

TL;DR

The recent "unified conversation thread" work (PRs #561–#563) is well built and renders cleanly — zero console errors, good empty states, inline recording player. But it shipped four event renderers and, measured across all 5,279 conversations, two render for literally zero conversations (inbound-call, appointment) and a third (blast-reply) is deduped to ~zero by design. The fixes are mostly data-wiring + presentation, not a rebuild.

Method: headless browser with a short-lived SUPERADMIN session minted on prod & invalidated after (verified 0 rows); plus SQL run against prod that reproduces the exact filter logic in lib/leads/*-events.ts. No messages/dispositions/writes.

1 · Event-kind coverage across all 5,279 conversations

Event kindThreads% of inboxStatus
SMS5,279100%✓ healthy
Blast (outbound)5,25899.6%⚠ in every thread
Call — softphone78914.9%✓ works
Call — dialer3176.0%✓ works
Call — inbound00%✗ dead — table empty
Blast reply (blast-in)~0~0%⚠ deduped to SMS
Appointment00%✗ dead — never linked
Appointment if linked2033.8%realistic ceiling

A kind "renders" iff it passes both its per-source fetch filter and the conversation-scoping filter in conversation-events.ts.

2 · It renders cleanly (the build is good)

inbox overview
Inbox overview — 5,244 open / 1,833 unread. Loads with zero console/page errors.
unified thread
Unified thread: a blast bubble (dashed indigo, campaign-labeled) at top, the inbound SMS reply, and two softphone call rows (0:03, 0:02) — all chronological. Note the two tiny non-connect call cards out-weigh the actual reply.
connected call
A connected 2:51 softphone call sits in-thread alongside a 0:07 non-connect.
recording player
Inline recording player (play / 1× / download) expands in place.

3 · Finding A — Appointment rows: 0% rendered, wrong label dead + incorrect

2,590 appointments exist; 0 carry a conversation_id, so the filter (conversation-events.ts:94) drops all of them. Root cause: the main booking path book-appointment/route.ts:203 never sets conversationId (only the AI path does, and it's booked ~none).

Label bug: real statuses are completed (2,455) and proposed (135) — but the helper maps updatedAt > createdAt → "Rescheduled". So 2,454 completed appointments would all render as an amber "Rescheduled," which is wrong. There's no handling for completed/no_show.

Reality check: even fully linked, only 203 conversations (3.8%) would ever show an appointment — most appts belong to leads with no inbox conversation. This is a correctness/dead-code issue more than a high-volume feature.

4 · Finding B — Blast bubble is in 99.6% of threads, but it's one bubble polish

Blasts are fetched lead-wide, so the lead's outreach shows in every conversation. My first guess ("wall of blasts") was wrong — the data says blasts/lead is p50=1, p90=2, max=4; only 27 conversations have a lead with >3 blasts, none over 10. It's almost always the single originating outreach at the top — useful context.

The real issue is visual weight: the dashed-indigo bubble + the BLAST · <FULL CAMPAIGN NAME> label repeats the campaign already shown in the header chip and lead panel. Lighten it; keep it.

5 · Finding C — ~3 of 4 call rows are non-connects noise

SourceRendered0 sec1–9 sec≥30 sec% under 10s
Softphone1,8741951,12035174.1%
Dialer31661484576.6%

Each — including 195 literal 0-second softphone calls — gets a full-width bordered card heavier than the actual reply bubble. Collapse sub-threshold attempts into "N call attempts · no answer," reserve the full card (+ player) for connected calls, and surface disposition (often blank).

6 · Findings D–F — dead inbound branch, blast-in dedup, no "load earlier"

7 · Prioritised recommendations

#ActionEffortValueWhy (data)
1Collapse non-connect call rows + show dispositionMHigh74–77% of call cards are <10s
2Lighten the seed blast bubble (label + treatment)SMed-High99.6% of threads; redundant label
3Appointments: link conversationId + fix status map, or removeMMed0% now, 3.8% ceiling, mislabels
4Remove or wire the dead inbound-call branchSMedinbound_calls empty
5Verify + simplify blast-in dedupSLow-Med7,449 replies, ~0 rendered
6"Load earlier" for long threadsSLowlatent truncation at 100
7Emil pass: calls/appts as quiet system markersMMedsystem cards out-weight messages

Start with #1 + #2 — highest value-to-effort, strongest data, pure presentation (no schema/migration). #3 is the biggest correctness cleanup.

Backing data & screenshots in the repo: docs/audits/inbox-unified-timeline-review-2026-06-03.md · docs/screenshots/inbox-prod-review/. Served gated behind the stage login.