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.
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.
Everything below was read from the code on 2026-09-03, not from memory.
| Route | What it renders | Preset | Who can reach it |
|---|---|---|---|
/clara/conversations | ConversationsLog, heading "All conversations" | none, the full superset | platform_admin only |
/leasing/conversations | same component | topics = leasing, tour | every role with leasing |
/maintenance/conversations | same component | topics = maintenance | every role with maintenance |
/tenants/conversations | same component | person type = residents (there is no "tenant" topic) | every role with tenants |
/conversations | redirect → /clara/conversations | query preserved | rides the conversations entity, then 403s at the target for customers |
/admin/dev/agent-traces | same component, staff firehose | includeTest, flat rows | staff |
src/components/feature/dashboard/conversations/ConversationsLog.tsx renders all of the above. The only difference between surfaces is the seed passed to two filters. A drift test (src/__tests__/conversations-surfaces-drift.test.ts, marked permanent) pins that contract.GET /api/conversations. Topic and person-type scoping is client-side seeding of the table state, and a ?filterTopics= or ?view= URL param already overrides the seed. So every subtab is already expressible as a deep link into one page./conversations as a root route showed no sub-rail and the page was moved under Clara "so the rail sub-nav persists". A flat top-level item with no sub-rail is a shape the rail already supports (Collections and Vendors are exactly that), so this constraint disappears once the page stops trying to live inside a section.permissions-source.ts grants the clara entity to platform_admin only, by design ("a PropFlow-staff observability surface, not an operator feature"). The unfiltered log inherits that. An org admin or PM at Situs has never had a page that shows every conversation Clara had on their behalf.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.
| Product | Top-level inbox? | Unified or split | AI handled vs needs a human | Link to the record |
|---|---|---|---|---|
| EliseAI | No "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-X | Yes, "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 IQ | Communication 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:
topics[] is a multi-valued array on the row. A page-per-topic model cannot represent it; a facet can.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.
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
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)
/conversationsConversationsLog with no preset. Heading "Conversations". Second item on the rail, right after Dashboard, because that is where operations people live (Fede's Eileen point) and where every competitor puts it.conversations permission entity, which every role already holds at READ or better. This is what makes the unfiltered list reachable by customers.| Old route | Redirects to | Then |
|---|---|---|
/leasing/conversations | /conversations?filterTopics=leasing,tour | Instrumented 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.
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.
| View | Filter it applies | Why it exists |
|---|---|---|
| Needs a human | escalated, or handled-by = human with no reply, or needs-a-look | The triage queue. The industry's primary surface. Candidate default for staff. |
| All | none | The hub. Candidate default for customers. |
| Leasing | topics = leasing, tour | Replaces the Leasing subtab |
| Maintenance | topics = maintenance | Replaces the Maintenance subtab |
| Residents | person type = residents | Replaces the Tenants subtab |
| Renewals · Collections | topics = renewal · billing | Sections 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.
| Touches | Files |
|---|---|
| Rail | src/components/primitives/nav-items.tsx: add one PRIMARY_ITEMS entry, remove four subtab entries, flip NAV_REDIRECT_STUBS for /conversations and /clara/conversations |
| Routes | src/app/(workspace)/(operations)/conversations/page.tsx becomes the page; leasing/conversations, maintenance/conversations, tenants/conversations, clara/conversations become instrumented redirect stubs |
| Landing cards | LeasingNavCards.tsx, MaintenanceNavCards.tsx, TenantsNavCards.tsx |
| Palette | palette-index.ts: five "Conversations" entries become one |
| Permissions | none: /conversations already resolves to the conversations entity |
| Tests | conversations-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 links | Anything 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.
Answered by Gera on 2026-09-03: the recommended option on all six. Answers save for everyone signed in.
nav-items.tsx, ConversationsLog.tsx, permissions-source.ts, conversations-surfaces-drift.test.ts, docs/planning/conversations-ia-consolidation.md, docs/planning/conversations-unify/standardization-plan.md.