One Conversations

Should Conversations be one top-level page instead of a subtab under Leasing, Maintenance, Tenants and Clara? The assessment, what the industry does, and the shape we should build.

2026-09-03 · DECIDED: all six on the recommended option (Gera, same day) · MERGED: #6948 on main (defab1ab, 2026-09-03 22:10 UTC, four review rounds); same evening #6969 flattens Tenants (merged 23:20 UTC) and #6968 moves the item after Clara (merged Sep 4 02:13 UTC) — all three on main · FOLLOW-UPS LANDED Sep 4 03:08–03:11 UTC: #7011 roster cross-pin, #7012 stub headers, #7013 query-safe resolver, agent-smith#377 Smith eval links · owner: Gera · pairs with Where Conversations Live (the detail-URL half)

Yes, and it is cheaper than it looks. The five conversation list surfaces are already one component, one endpoint, one filter set. Collapsing them is a navigation change: one new rail item, four subtabs deleted, four redirects. The data layer does not move. The one real problem it fixes is not tidiness: today the unfiltered list lives under the staff-only Clara section, so no customer role can see all of their conversations in one place. A top-level page fixes that for free.

The decision as it was made (standup, Sep 2)

From the PropFlow standup transcript, Gera and Fede, 2026-09-02 16:23 CDT:

Fede: The other thing that annoys me is I don't know why we have Clara conversations, and then we have leasing conversations… Should there just be one global? Like, here's all the conversations.

Gera: That's what I was mentioning early on, and the leasing conversations is literally the Clara conversations with the filter. That's all it is.

Fede: Less tabs and… less things to learn.

Gera: Should I just go ahead and remove all the conversations pages? Fede: Yeah. Gera: Just less noise. Clean.

Fede: Maybe conversations like a top level item here. Gera: Not even under Clara.

Fede: If I'm the owner, I'm probably hanging out on the dashboard more. But if I'm in operations, like Eileen, she's probably gonna be living more on the conversations.

Two side notes from the same exchange, not part of this design: prospect rows in the list lack the hover hyperlink that vendor and tenant rows have (Smith was asked to fix it), and Fede read the blue "resident" dot as a read/unread marker. The second one is worth a look when the page is rebuilt, because a unified list makes the row identity marker more load-bearing, not less.

Where we actually are

Everything below was read from the code on 2026-09-03, not from memory.

RouteWhat it rendersPresetWho can reach it
/clara/conversationsConversationsLog, heading "All conversations"none, the full supersetplatform_admin only
/leasing/conversationssame componenttopics = leasing, tourevery role with leasing
/maintenance/conversationssame componenttopics = maintenanceevery role with maintenance
/tenants/conversationssame componentperson type = residents (there is no "tenant" topic)every role with tenants
/conversationsredirect → /clara/conversationsquery preservedrides the conversations entity, then 403s at the target for customers
/admin/dev/agent-tracessame component, staff firehoseincludeTest, flat rowsstaff

What the industry does

Verified from product pages, help centers and press on 2026-09-03. EliseAI, Yardi and Buildium help centers are bot-gated, so those rows lean on search snippets of the articles; nothing is guessed, gaps are marked.

ProductTop-level inbox?Unified or splitAI handled vs needs a humanLink to the record
EliseAINo "Conversations" item. Rail: Tasks, Contacts, Phone, Settings, Knowledge, Onboarding.Split by person (Contacts → Prospect / Resident), never by product. Maintenance updates live in the resident's thread.Task Queue with typed tasks: Needs Reply, Call Back, Missed Call, Follow Up, Post-Tour Follow Up. Mobile app is "only the conversations and tasks that require a human".Task or contact opens the guest card + conversation.
Funnel"My Queue" is the work surface (help center is login-walled).Unified: "every conversation, on every channel, logged to the same renter record", prospects and residents.Role-based prioritised queue; specialty queues (renewal team).Guest card is the hub.
Knock (RealPage)Yes, product page is literally /product/inbox/.Unified per contact across text, email, chat, social, phone."Transition conversations from AI to a leasing agent when human intervention is needed."One guest card, one screen.
AppFolio Realm-XYes, "a central inbox".Unified across residents, owners, investors.AI sorts and suggests; humans send.Message → action (e.g. a work order).
Entrata OXP (Jun 2026)Yes, "a shared team inbox across leasing and operations".Explicitly unified across leasing + ops and across human + agent-led threads. Names "separate inboxes and handoffs" as the problem."Escalations & Playbooks: task-based routing for tasks that require human attention"; "handoff visibility", "triage ownership".Universal Profile: conversations + open work orders + renewals on one record.
Yardi CRM IQCommunication Hub, "follows agents from screen to screen".Unified: prospect, applicant, resident, all channels."No separate AI dashboard, no context lost in the handoff."Single activity view.
Buildium"Communication" top-level menu.Unified by audience (tenants, owners, vendors).No AI triage.History tied to the profile.
Intercom / Front / HubSpot (reference)One Inbox, top-level.One inbox, many saved Views.The AI is a filter attribute on the same inbox (Intercom "Fin involved"), not a separate page. Default views: All, Unassigned, Assigned to me.Side panel.

Three arguments that carry the decision:

  1. Ownership and escalation live in one list. The industry vocabulary for "Clara is done, a human is needed" is a single triage queue. Sectioned inboxes split that queue four ways and recreate exactly the "unclear ownership" Entrata calls out.
  2. Topic is a property of the conversation, not of the page, and it switches mid-thread. A resident who asks about a leak and then about renewal is one thread with two topics. Our own model already says this: topics[] is a multi-valued array on the row. A page-per-topic model cannot represent it; a facet can.
  3. Cross-channel continuity needs one thread per person. Every vendor's headline ("never repeat themselves") depends on it. Per-section tabs push the same SMS thread into several places.

The one real argument for the sectioned shape, context in place, is satisfied by something we already have: the conversation panel on the work order, prospect, tenant and turnover detail pages. That is different from a per-section inbox and stays.

The shape to build

Rail today

Dashboard
Properties      Overview · Communications · Calendar
Leasing         Overview · Prospects · Tours · Renewals · Conversations
Maintenance     Overview · Work Orders · Turnovers · Routine · Costs · Conversations · Calendar
Collections
Tenants         Overview · Tenants · Conversations
Vendors
Clara (staff)   Command Center · Conversations

Rail proposed

Dashboard
Conversations   flat, no sub-rail, every role
Properties      Overview · Communications · Calendar
Leasing         Overview · Prospects · Tours · Renewals
Maintenance     Overview · Work Orders · Turnovers · Routine · Costs · Calendar
Collections
Tenants         Overview · Tenants  (or flat, see D4)
Vendors
Clara (staff)   flat = the command center (see D3)

1. One page, /conversations

2. The subtabs become deep links, then nothing

Old routeRedirects toThen
/leasing/conversations/conversations?filterTopics=leasing,tourInstrumented with the redirect-hit counter from #6837, deleted when the detector says hits have drained. Same retirement ladder the convos session is already running for the detail routes.
/maintenance/conversations/conversations?filterTopics=maintenance
/tenants/conversations/conversations?view=residents
/clara/conversations/conversations (query preserved)

The section landing pages each carry a "Conversations" nav card today. Those can stay as deep links with the preset applied (zero cost, keeps the "arriving from Maintenance shows work-order threads" behaviour) or go, per D5 below.

3. Views, not tabs

The subtabs did one useful thing: a one-click preset. On the unified page that is a row of view pills above the table, each one a saved filter combination, reading and writing the same URL params the table already uses. Nothing is stored server-side.

ViewFilter it appliesWhy it exists
Needs a humanescalated, or handled-by = human with no reply, or needs-a-lookThe triage queue. The industry's primary surface. Candidate default for staff.
AllnoneThe hub. Candidate default for customers.
Leasingtopics = leasing, tourReplaces the Leasing subtab
Maintenancetopics = maintenanceReplaces the Maintenance subtab
Residentsperson type = residentsReplaces the Tenants subtab
Renewals · Collectionstopics = renewal · billingSections that never had a conversations subtab get one for free

Later, when there is an assignee on the row, "Assigned to me" and "Unassigned" join the row. There is no assignee field today; "Handled by" is derived. That is a separate piece of work and not a prerequisite.

4. What does not move

Blast radius

TouchesFiles
Railsrc/components/primitives/nav-items.tsx: add one PRIMARY_ITEMS entry, remove four subtab entries, flip NAV_REDIRECT_STUBS for /conversations and /clara/conversations
Routessrc/app/(workspace)/(operations)/conversations/page.tsx becomes the page; leasing/conversations, maintenance/conversations, tenants/conversations, clara/conversations become instrumented redirect stubs
Landing cardsLeasingNavCards.tsx, MaintenanceNavCards.tsx, TenantsNavCards.tsx
Palettepalette-index.ts: five "Conversations" entries become one
Permissionsnone: /conversations already resolves to the conversations entity
Testsconversations-surfaces-drift.test.ts (the permanent sensor, rewritten to pin the new contract, not deleted), nav-items.test.ts (one-page-one-rail and canonical-href pins), entity-conversation-routes.test.tsx
Outbound linksAnything that mints /leasing/conversations?… or /clara/conversations?… into Slack or email. The #6837 counters will name them; the convos session already found and fixed two.

Estimated size: one PR, under a day, no migration, no API change, no new component. The optional pieces (D4 flattening, view pills) are each their own small PR. Built and merged 2026-09-03 as #6948. Review added two things the assessment missed: the page and its retired stubs must not inherit the leasing-prospects module flag the conversations entity carries (an org with prospects off would have had a dead rail item), and the stubs must ride the conversations entity rather than their old section so a stale link is never gated stricter than its destination. Follow-ups left on the record: cross-pin the third stub roster in permissions.ts against the other two, name that registration in the stub headers, strip query strings in routeToEntity the way routeToModuleFlag now does, and agent-smith#369 for the links Smith still mints. All four follow-ups merged 2026-09-04: #7011 (the roster cross-pin test), #7012 (stub headers name the third registration), #7013 (routeToEntity strips query and fragment before matching), and agent-smith#377 (the eval scorer builds its conversation link through the one builder; #369 landed earlier the same night). Nothing on the record is left open.

Decisions

Answered by Gera on 2026-09-03: the recommended option on all six. Answers save for everyone signed in.

D1 · Where on the rail?

D2 · What does the page open on?

D3 · What happens to the Clara section?

D4 · Tenants with one subtab gone

D5 · The "Conversations" card on the Leasing / Maintenance / Tenants landing pages

D6 · Sequencing

Sources

PropFlow Docs