Skip to main content

WiseAI Realtor — Command Center (Dashboard) Expected Output Spec

⛔ STATUS: DRAFT — NOT APPROVED. CLAUDE.md Rule #17 (HARD GATE) is NOT satisfied.​

Do not start the customer-facing build of this screen until the founder approves this spec. It is Stage-1 agent research, pre-populated from the realtor design docs + the dashboard.html mock; the founder confirms or changes each expected output in a Stage-2 interview.

Sourced from (read-only, 2026-06-29): products/wiseai-realtor/design-direction.md (Workflow-Rail shell, bento command center, anti-patterns, components), information-architecture.md (group A "Daily Work"), data-model.md (the re_* CRM spine), backend-completeness-audit.md (the systems-layer gaps + Canadian bar), and the visual prototype realtor-mockups/dashboard.html. Format matched to the sibling specs acceptance/ai-front-desk.md and acceptance/reviews-engine-mvp.md.


0. Scope — what this spec covers, and what it does NOT​

This spec covers the Command Center / "Today" dashboard of the WiseAI Realtor backend — the bento command center an agent or team owner lands on (the /realtor/app shell on the wiseaiagency.com host; verify the exact mount against the shipped app router per do-not-reinvent.md). It is one of four sibling specs that together cover the Daily-Work core:

SpecScreenMock
thisDashboard (bento command center)realtor-mockups/dashboard.html
realtor-leads-mvp.mdLeads (list + Kanban + drawer)leads.html
realtor-contact-record-mvp.mdLead / contact record (two-tier)record.html
realtor-inbox-mvp.mdConversation inbox (3-pane)inbox.html

This spec does NOT re-specify: the global Workflow-Rail shell chrome (left nav rail, top bar, command palette, autopilot toggle widget) beyond how the dashboard consumes it; the underlying voice/chatbot capture behaviour (that is acceptance/ai-front-desk.md, the approved sibling for the same multi-tenant agent); the Reviews/Automations engine (acceptance/reviews-engine-mvp.md); or the public agent website (/s/[slug], already shipped). Those are assumed and cross-referenced.

The RE Pro Website, listings, chat/voice, Listing Studio and Reviews Engine already shipped to main (PR #1017 + follow-ons). This is a backend command center on top of that — extend, do not rebuild (do-not-reinvent.md).

The re_* CRM spine is NEW and being built (WS1a). Today RE leads write to the flat local_business_leads table via createLead(); the re_* tables are proposed in data-model.md and nothing reads them yet. Every "reads X" claim below is a target-state read of the CRM spine (Phase-A forward-fill: re_contacts/re_interactions become the read model while local_business_leads stays the capture buffer — data-model.md §2.2). Until the spine ships, tiles fall back to local_business_leads aggregates or the honest empty state.


1. The mock screen this spec governs + what it reads​

Governs: realtor-mockups/dashboard.html — a Workflow-Rail shell (navy left rail + top bar) with a 12-column bento grid: a gold-accented hero tile (the one daily number), a 4-tile KPI strip, a large "Aria — your AI front office" activity proof tile, a "Call these 5 today" Follow-Up Coach tile, a "New leads by source" ROI tile, a Pipeline snapshot, and a "Needs a human" escalation tile. Top-bar AI: Autopilot toggle, Density (Comfortable/Compact) control, "Today / Insights / Goals" tabs.

Reads (target-state CRM-spine APIs — WS1a; verify exact route names against the shipped lib, do not fabricate):

TileUnderlying read
Hero "New leads today"re_contacts created today (or local_business_leads fallback), scoped by role
KPI: AI conversations / Calls answered / QR scans / Showings bookedre_interactions by interaction_type/channel for the period; calls from voice logs; QR from scan events
"Aria — your AI front office" activity feedre_interactions WHERE actor='ai_agent' ORDER BY occurred_at DESC
"Call these 5 today" Follow-Up Coachre_tasks (due/overdue) + re_contacts keep-in-touch triggers (birthday, home_anniversary, slipping)
"New leads by source" ROIre_contacts.source_category rollup for the period
Pipeline snapshotre_contacts.pipeline_status counts + re_deals weighted value
"Needs a human"re_interactions/conversations flagged escalated, not yet taken over

Demo tenant: Terry & Sheri Real Estate (local_businesses.id = 00000000-0000-4000-a000-000000000c01, slug terry-and-sheri, vertical = real_estate) — the canonical Beckett/Sutton/Woodstock demo. NEVER use a real customer tenant as a write target (feedback_never_modify_customer_data). The mock's "Team Beckett / Sheri Beckett / Sutton Group · Woodstock" identity maps to this tenant.


2. The AI-Bridge / honesty anchor (applies to every tile)​

Per knowledge/architecture/ai-bridge-principle.md:

  • Aria is a bridge, named and transparent. The AI front office is "Aria" (locked, per project_wiseai_realtor_system). Every tile that shows AI work attributes it to Aria/AI honestly (the activity feed says "by Aria (AI voice)", not "by the office"). The "Needs a human" tile exists because the AI escalates rather than pretending — it never hides that a human is required.
  • Honest metrics only. Every number is a real count from the spine — no "Delivered"/"Opened"/fabricated engagement theater (outbound process map). A metric with no data source connected reads "Not measured — not yet connected" (the ai-front-desk.md §9 honesty rule), never a fake number.
  • No advice surfaced as fact. Dashboard copy never states home values, legal/mortgage/tax positions, or steering language; AI suggestions are framed as next actions for the human agent, not advice to a client.

3. Role-based visibility (capability-gated — rbac.ts)​

WiseAI Realtor is multi-user. The implemented RBAC is rbac.ts (PR #1070, live + tested — the source of truth), which gates by capability across the roles agent | team_admin | transaction_coordinator | broker_admin | brokerage_owner | marketing_assistant | isa | external_partner at scopes own | team | brokerage (data-model.md §1.7 carries the same enum). The dashboard scopes by capability:

BucketRoles (rbac.ts)Dashboard scope
Brokerage managementbrokerage_owner, broker_admin (brokerage scope; all pipelines)Brokerage-wide: all agents' leads/pipeline/conversations; team-vs-member performance; pipeline value + commission/GCI — brokerage_owner edits (commissions:amount:edit), both read at brokerage scope
Team managementteam_admin (team scope; all pipelines in team)Team-wide: the team's leads/pipeline; team performance; pipeline value + commission/GCI read at team scope
Agentagent (own scope)Own book: own re_contacts/re_deals/tasks + pipeline value; own-deal commission/GCI visible (commissions:amount:read:own); no peer's pipeline or commission
ISAisa (own/assigned scope)Own/assigned leads + conversations; NO commission/GCI
Support / externaltransaction_coordinator (deals + team-read), marketing_assistant (content/reviews), external_partner (read-only, bounded)Feature-limited subset; never commission/GCI

Team Beckett note: Terry and Sheri are both management roles — e.g. brokerage_owner / team_admin (team = one local_businesses tenant, two agents — data-model.md §1.7). The mock shows Sheri seeing team-wide activity, which is correct for her role. The agent-scope behaviour must be verified by adding a non-owner agent (e.g. a buyer ISA) to the demo tenant and confirming the narrowing.

State table:

StateConditionDashboard behaviour
Provisioned, has datatenant active, re_* populatedFull bento (§4) scoped by role
Provisioned, no data yettenant active, zero contacts/interactionsDemo-seeded empty state + onboarding checklist (§5), not blank tiles
Loadingdata in flightSkeletons per tile, not a full-page spinner (§5)
A metric source unconnectede.g. voice line not provisionedThat tile reads "Not measured — not yet connected" (honesty rule)
Paused / cancelled tenantlocal_businesses.status ∈ {paused, cancelled}Read-only dashboard; capture is off (consistent with ai-front-desk.md §V9 / local-business-platform.md)

4. Expected outputs — per bento region​

4.1 — Hero tile (the ONE daily number)​

Should see:

  • A single 2×-scale tile, top-left, gold left-edge accent, with one hero metric and a 30–44px tabular numeral — default "New leads today" with a trend vs the prior 7 days and a sparkline (design-direction "KPI tile" + bento hero). It is the only number at this scale (anti-pattern #2 — size by importance; ≤12 tiles, curate to 5–9 prominent).
  • An embedded urgent callout when applicable: e.g. "3 hot leads awaiting a callback · longest waiting 18 min · captured by AI voice overnight" with an Open → action. This is the speed-to-lead make-or-break surface (audit P1).
  • The hero number is role-scoped: management sees team-wide new leads; an agent sees their own.

Should NOT see:

  • A wall of equally-sized tiles with no hero (anti-pattern #2).
  • A fabricated number when the tenant has no leads (→ empty state, §5).
  • Commission/GCI as the hero for an agent-scope user.

Reads: re_contacts created in the period (role-scoped); urgent = unactioned hot leads (lead_score high, no outbound re_interactions yet) with the longest wait computed from MIN(inbound occurred_at).

Success: the agent sees, in one glance, the single number that should drive their morning, and a one-click path to the most time-sensitive work.


4.2 — KPI strip (the curated tile set)​

Should see:

  • A small set (mock shows 4) of secondary KPI tiles, each a label + big tabular number + a small honest trend: AI conversations, Calls answered by AI, QR sign scans, Showings booked. Deeper analytics live behind the Insights tab (not crammed onto Today).
  • Each tile's number is real or "Not measured" — e.g. "Calls answered by AI" reads "Not measured — not yet connected" until a local_business_voice_lines row is provisioned (mirrors ai-front-desk.md §9; do not show "100% pick-up" with zero calls).

Should NOT see:

  • More than ~10 tiles total on Today (anti-pattern #2).
  • "Showings booked" implying the AI booked an appointment when no scheduler exists. ⚠️ OPEN — flag, do not silently resolve: the mock shows Aria "booked 3 showings", but the approved ai-front-desk.md (LOCKED decision 4) is capture + FAQ + escalate only — the AI does NOT book/schedule, and the audit lists a "Showing scheduler (Cal/Google)" as named, not designed. Until a scheduler ships and an appointments decision is made for the RE vertical, this tile must either be hidden or relabelled to a captured signal (e.g. "Showing requests captured"), and must not claim the AI confirmed a booking. Founder to resolve in the Stage-2 interview.

Reads: re_interactions grouped by interaction_type/channel; voice call counts from the voice line; QR from scan events.

Success: four honest secondary numbers; no theater; nothing implying unbuilt capability.


4.3 — "Aria — your AI front office" activity proof tile​

Should see:

  • A large tile titled "Aria — your AI front office" with a Working · Autopilot status pill (reflecting the autopilot toggle, §4.7) and a one-line overnight recap, then a reverse-chronological feed of what Aria did: booked/requested-showing, answered+qualified a call, captured a lead from a QR scan, and "Needs you:" items Aria escalated (e.g. a commission question — "outside Aria's lane").
  • Each row is honestly attributed to AI, carries a language chip where relevant (e.g. ਪੰ Punjabi — auto-captured re_contacts.preferred_language, never hardcoded), a relative timestamp, and links to the underlying contact/ conversation. A View log → opens the full activity.

Should NOT see:

  • Activity attributed to "the office"/a human when Aria did it (AI-bridge transparency).
  • An escalation row that disappears without a human path (it must route to the "Needs a human" tile / inbox).

Reads: re_interactions WHERE actor='ai_agent' (and escalation flags), ORDER BY occurred_at DESC, role-scoped.

Success: the agent trusts the AI because they can see exactly what it did and where it correctly stopped and handed off.


4.4 — "Call these 5 today" — Follow-Up Coach​

Should see:

  • A tile titled "Call these 5 today" with a Follow-Up Coach label and a short, prioritized list mixing keep-in-touch triggers (birthday today, 1-yr move-in anniversary), at-risk leads (Slipping — viewed a listing 4× / no reply 2 days), and Hot AI-qualified leads — each with a one-tap Call action and the reason it surfaced.
  • Language chips where the contact's preferred_language is non-English (drives "call in Punjabi/French" prep).

Should NOT see:

  • A generic "you have N tasks" with no reasoning (the value is why these 5).
  • Past clients with do_not_contact = true (global suppression gates this list — §4.8 / data-model.md §1.1).

Reads: re_tasks (due/overdue, AI-coach-created) + re_contacts triggers (birthday/home_anniversary/last_touch_at decay) — excluding suppressed contacts.

Success: the keep-in-touch engine (audit P1 "AI Follow-Up Coach") gives the agent a defensible call list with a reason for each.


4.5 — "New leads by source" — ROI tile​

Should see:

  • A ranked bar list of lead sources for the period with count and cost/lead: e.g. QR yard signs (14 · $0/lead), Website/AI chat (11 · $0/lead), REALTOR.ca (7 · $31/lead), Google Ads (5 · $44/lead), Referral (3 · $0/lead) — the "which X creates leads / at what cost" value narrative (audit — the five reports).
  • AI-owned sources (QR, AI chat/voice, referral) honestly shown at $0/lead to make the wedge visible; paid portals carry their real cost/lead.

Should NOT see:

  • Made-up cost figures where no ad spend is connected (show count only, cost "—", until spend is wired).

Reads: re_contacts.source_category rollup (taxonomy data-model.md §4).

Success: the agent can see at a glance which channels their money and the AI are actually producing leads.


4.6 — Pipeline snapshot + "Needs a human"​

Pipeline snapshot — should see:

  • A compact stage grid (New / Contacted / Nurturing / Appointment / Offer / Won MTD) with counts and colorblind-safe stage colors paired with labels (design-direction pipeline palette), plus a weighted value in pipeline total. Open board → deep-links to the Leads Pipeline view.
  • Value is role-scoped: management sees team weighted value; agent sees own.

"Needs a human" — should see:

  • A short list of conversations the AI escalated and a human has not taken over yet — each with channel chip (Chat/Voice/SMS), language chip, timestamp, a one-line reason ("What's your commission on a $700k sale?" — Aria escalated), and a Take over → / Listen + call → action into the inbox (realtor-inbox-mvp.md).

Should NOT see:

  • A pipeline value that leaks team/peer commission to an agent (who may read only their OWN, commissions:amount:read:own).
  • An escalation that is older than its SLA without visual urgency.

Reads: re_contacts.pipeline_status + re_deals (value); escalated re_interactions/conversations not yet taken_over.

Success: the owner sees the book's shape and the few conversations that need a person right now, with one click to act.


4.7 — Autopilot / co-pilot toggle (top bar, reflected on the dashboard)​

Should see:

  • A top-bar AI: Autopilot ⇄ Co-pilot switch that sets how autonomous the AI front office is (design-direction component; AI-bridge fit). When Autopilot, the Aria tile shows "Working · Autopilot"; when Co-pilot, the AI drafts but waits for the agent to send/act, and the dashboard reflects "drafts awaiting you".
  • The setting is remembered and (for management) settable per the dashboard; per-conversation override lives in the inbox (realtor-inbox-mvp.md).

Should NOT see:

  • A toggle that silently does nothing (it must change the AI behaviour the tiles describe — verify behaviour at ≥2 timepoints, not DOM presence, feedback_verify_behavior_not_dom_presence).

Success: the agent controls AI autonomy and the dashboard honestly reflects the chosen mode.


Should see / NOT see:

  • No dashboard surface ever offers an action that would contact a do_not_contact = true contact or one whose channel consent is withdrawn / expired (re_contacts consent rollup, data-model.md §1.1/§1.3). The Follow-Up Coach and any "send"/"call" affordance excludes suppressed contacts — the global suppression gates every outbound surface incl. the AI (audit P0 #3). No cron may auto-send (feedback_never_cron_send_campaigns).

5. Empty / loading / error states (anti-patterns #6 and #7)​

A build that ships blank tiles or spinners is not done (design-direction anti-patterns #6 "blank empty states", #7 "spinners/pessimistic UI").

StateExpected output
First-run / no dataBento renders with a demo-seeded preview + a one-CTA onboarding checklist ("Connect your voice line", "Import past clients", "Add your first listing") — never empty tiles. The hero shows "0 new leads today — here's how to get your first" with the checklist, not a blank box.
LoadingPer-tile skeletons (shimmer placeholders), optimistic, <100ms transitions — not a full-page spinner.
A source unconnectedThe affected tile reads "Not measured — not yet connected" with a connect link; other tiles still render.
Read error / API downThe affected tile shows a non-destructive inline error ("Couldn't load — retry") with a Retry; the rest of the dashboard stays usable. Never a white screen.
Paused / cancelled tenantRead-only banner; capture-driven tiles show last-known with a "service paused" note; no actions that would send/capture.

6. Accessibility (AODA → WCAG 2.1 AA)​

Per the audit's compliance must-haves ("AODA → WCAG 2.1 AA on public sites AND admin"):

  • Contrast: all text meets ≥4.5:1 (≥3:1 large). Teal fill carries white text at ~4.8:1; gold is highlight/accent only, never body text (design-direction color rule). Trend red/green and pipeline stage colors are paired with a label/icon (never color-only) — colorblind-safe.
  • Keyboard: every tile action (Open, Take over, Call, toggle, tab switch, density switch) is reachable and operable by keyboard with a visible focus ring; the Cmd+K palette is keyboard-first.
  • Semantics: KPI tiles expose label+value to assistive tech (not just a bare numeral); the autopilot toggle is a labelled switch with state; the activity feed is a list with meaningful row text; numerals use tabular-nums.
  • Motion: sparkline/skeleton animation respects prefers-reduced-motion.
  • Targets: comfortable density ~44px rows (default); compact toggle does not drop below AA target sizes for interactive controls.

7. Acceptance checklist (QA runs on the deployed URL)​

Evidence is behavioural verification on the deployed wiseaiagency.com host (real host — host-aliasing/middleware applies; preview hosts hide rewrites, feedback_preview_host_hides_middleware_rewrites) against the demo tenant (…0c01) — never "build passes". Sample state at ≥2 timepoints; assert change.

// Hero + KPIs
test.fixme('hero shows ONE daily number (new leads today) at 2x scale, top-left, with trend + sparkline', () => {});
test.fixme('urgent callout surfaces oldest unactioned hot lead with a working Open → into the lead', () => {});
test.fixme('a metric with no connected source reads "Not measured — not yet connected", never a fake number', () => {});
test.fixme('"Showings booked" does NOT claim the AI confirmed a booking (no scheduler) — hidden or relabelled', () => {});

// AI activity + coach + escalation
test.fixme('Aria activity feed attributes each row to AI honestly and links to the contact/conversation', () => {});
test.fixme('Follow-Up Coach lists prioritized contacts each WITH a reason; excludes do_not_contact contacts', () => {});
test.fixme('"Needs a human" lists escalated, not-yet-taken-over conversations with a working Take over →', () => {});

// Role scope (RBAC)
test.fixme('a brokerage_owner/broker_admin/team_admin sees team/brokerage pipeline value + commission/GCI at their scope', () => {});
test.fixme('an agent sees only their own book; OWN-deal commission/GCI visible (read:own) but never a peer/team commission; isa sees none', () => {});

// Autopilot
test.fixme('toggling Autopilot⇄Co-pilot changes AI behaviour the tiles describe (verify at 2 timepoints)', () => {});

// States + a11y
test.fixme('first-run tenant shows demo-seeded preview + onboarding checklist, never blank tiles', () => {});
test.fixme('loading shows per-tile skeletons, not a full-page spinner', () => {});
test.fixme('keyboard-only user can reach every tile action; focus rings visible; reduced-motion honored', () => {});
test.fixme('no color-only meaning — trend + pipeline stages carry a label/icon; gold is never body text', () => {});

8. Guardrails for agents building against this spec​

  • Rule #17 not satisfied — do not build until the founder approves.
  • Extend, don't rebuild — reuse the shipped RE Pro Website / leads / AI / Listing Studio / Reviews code (do-not-reinvent.md); the dashboard is a new read surface over the re_* spine, not a new product.
  • Migrate-before-use — the re_* tables are PROPOSALS (data-model.md); none are applied by this spec. A column is not "used" until it exists; verify via information_schema.columns (Rule #18). DDL is founder-gated, additive, IF NOT EXISTS, service-role-grant-only.
  • RBAC is load-bearing (capability-gated, rbac.ts) — commission/GCI is the highest-sensitivity data: only brokerage_owner edits; broker_admin/team_admin read at scope; an agent reads their OWN (commissions:amount:read:own); isa/support/external see none; a peer's commission must NEVER leak. Gate at the API + query layer; add a contract test like tier-config.contract.test.ts (data-model.md §5.2).
  • Honest metrics only — real counts or "Not measured"; no Delivered/Opened theater; no cron sends.
  • Verify on the real host — wiseaiagency.com (host-aliasing); behaviour at ≥2 timepoints, not DOM presence. Evidence-or-nothing (Rule #20).
  • If code diverges from this spec, the spec is updated first (founder approval), then the code.

End of spec. STATUS: DRAFT — NOT APPROVED. Open items for the Stage-2 founder interview: (1) the "Showings booked" / AI-booking conflict vs ai-front-desk.md decision 4; (2) the exact hero metric default; (3) which secondary KPIs make the Today set; (4) the role→scope mapping for non-owner agents on the Beckett tenant.