FuneralWiseAI — Dashboard Command Center Expected Output Spec
STATUS: DRAFT — NOT APPROVED. CLAUDE.md Rule #17 (HARD GATE) is NOT satisfied.
Stage-1 agent draft, pre-populated from the approved realtor design system (
design-direction.md), the shipped "Grace bento" Home body (FuneralDashboardBody.tsx, commitsccf0727c6+119b1ddbf), the funeral vertical spec (funeralwiseai-vertical.md), and the approved plan (PLAN_FUNERALWISE_DASHBOARD_REVAMP_2026-07-02.md). Do not build until the founder approves and the §11 open items are confirmed in a Stage-2 interview.
AI Bridge Principle applies to every AI-authored surface in this spec. The agent is a bridge to the funeral director, never a replacement; the dashboard surfaces that bridge honestly (labelled AI vs human, "flag for director" everywhere, no fabricated activity). See
architecture/ai-bridge-principle.md.
0. Scope — what this spec covers, and what it does NOT
In scope (Phase 1 + Phase 3 of the plan):
- The funeral-only application shell — a persistent left nav rail + top bar + scrollable content area (the realtor "Workflow-Rail" pattern, dressed in the FuneralWiseAI brand). Rail collapse, mobile off-canvas drawer, per-destination badge counts, top-bar setup-progress pill + Grace launcher + notification bell.
- The restyled per-screen archetypes: Home (bento — already shipped, kept), Inbox (3-pane), At-Need Calls (list + right drawer with crisis-urgency treatment), Pre-Planning (list + drawer).
- The funeral token mapping (realtor structural roles → FuneralWiseAI colors).
- The honest-metrics + demo-seeded empty-state contract for every tile/list.
?tab=deep-link compatibility (existing tab param keeps working under the new rail) + AODA (status never conveyed by color alone).
Explicitly OUT of scope (deferred / other specs):
- Grace concierge behavior — its own spec (
grace-onboarding-concierge-mvp.md); this spec only defines its launcher affordance in the top bar / tiles. - Setup-screen restyle (Train AI, Service Catalog, Website, Subscription) — Phase 4, a later same-series PR; day-1 those bodies render unchanged inside the new frame.
- Church / vet admin restyle — the rail is funeral-gated; church + vet keep the existing tab shell, pixel-unchanged (§7 is a hard regression gate).
- Voice-agent runtime / deploys (LIFE-SAFETY,
voice-agent-engineer+ founder go), dark mode (specced in the realtor system, not implemented), a working Cmd+K beyond a display affordance, and any email/notification send.
Build posture (do-not-reinvent): the funeral dashboard is the shared church
admin shell (admin/[token]/page.tsx → AdminShell → AdminDashboard),
hostname-switched to the funeral vertical (middleware.ts +
resolveFuneralToken() against premium_funeral_homes) with funeral tab bodies
swapped in when verticalProfile.key === 'funeral'. This spec wraps that
existing dashboard in a new funeral-gated frame and restyles the tab bodies —
it does not fork a second dashboard, and it does not touch the church/vet path.
The realtor shell (src/components/realtor/shell/) is the pattern source, read
for reference; the funeral shell is a build-to-spec port with funeral tokens (the
realtor components use inline hex and realtor IA — do not import them).
1. Design tokens — FuneralWiseAI mapping (realtor role → funeral value)
The realtor design system assigns three brand roles — chrome (navy), the single primary action accent (teal), and highlight/celebration (gold) — and lets neutrals carry 90% of the structure ("color as punctuation"). FuneralWiseAI keeps that structure and swaps the three brand values:
| Role (realtor) | Realtor value | Funeral value | Notes |
|---|---|---|---|
| Rail / chrome | navy #0B1F3A | slate #1E293B, active/deep #0F172A | The rail, top bar chrome, headers. |
| Primary action (the ONE accent) | teal #0F766E | deepened sage #3F685C (fill, white text), tint #EDF5F1 | Buttons, active-nav marker, links-on-white. Deepened from brand sage #4D7C6F for AA. See contrast note below. |
| Highlight / celebration ONLY | gold #C49A3A | copper #B45309, tint #FEF3E2, text-on-tint #8A4A0B | Left-edge accent / badge only. NEVER body text, never a plain label color (realtor anti-pattern #4). |
| Neutrals / semantic / type / spacing / radius / density | — | identical to the realtor spec | bg #FAFAFA, surface #FFFFFF, border #E6E8EB, text #18181B, muted #6B7280; Inter; 14px body; tabular-nums on all metrics; border-first low-shadow; comfortable (~44px) rows with a compact toggle. |
Sage contrast (AODA gate, verified). The primary action color must reach
≥4.5:1 for white text (WCAG AA normal text). Brand sage #4D7C6F measures
~4.75:1 on white — a bare pass with no margin. This spec adopts the deepened
sage #3F685C, which measures ~6.3:1 on white (comfortable AA, and AA-large
headroom), as the action fill. #4D7C6F may still be used for the sage tint
#EDF5F1 and non-text accents. The build must not ship a sage that a contrast
checker scores below 4.5:1 for its white-text usage.
Arrangement / pipeline stages (chip + list header, always label + color, colorblind-safe — realtor rule): New inquiry = blue, Arrangements = sage/teal, Service set = amber, Completed = green, Aftercare = violet. A stage is never conveyed by color alone (AODA §6).
Anti-patterns (from design-direction.md, apply verbatim): no tab-sprawl (one
record, AI summary on top); size tiles by importance (≤12, curate to 5–9); density
toggle (no 27" assumption); highlight color is never text (copper ban); same IA
across breakpoints; no blank empty states (demo-seeded + one CTA — §6);
skeletons not spinners; color as punctuation; drawers/command-palette over
modal-on-modal; border-first flat chrome (no gradients/glossy).
2. The application shell (funeral-gated frame)
2.1 — Gating (funeral only; church/vet untouched)
Should see / NOT see:
- The FuneralShell renders only when the resolved vertical is funeral
(
verticalProfile.key === 'funeral', i.e. apremium_funeral_homes-backed token on thefuneralwiseai.comhost). Church and vet admin render the existing tab shell, byte-for-byte unchanged (§7 regression gate). - The funeral dashboard owns its chrome — no ChurchWiseAI marketing header, no
sacred-gold/navy, no PewSearch panel (matches
funeralwiseai-vertical.mdD1).
2.2 — Left nav rail (slate)
Should see:
- A persistent left rail (slate
#1E293B, ~248px), collapsible to a ~64px icon rail. The collapse state is remembered (localStorage) and hydration-safe (read viauseSyncExternalStore— no server/client mismatch flash; realtor hydration pattern). - A brand tile at the top (FuneralWiseAI lockup / funeral home name), the primary destinations promoted from the existing 8 funeral tabs to rail items (Home, Inbox, At-Need Calls, Pre-Planning, Train AI, Service Catalog, Website, Subscription — final IA confirmed in the Stage-2 interview, §11), and a Settings footer item.
- The active destination marked with the sage accent (fill or left-edge marker) + a non-color cue (bold label / filled icon) — never color alone (AODA).
- Badge counts on the destinations that have actionable volume: At-Need
(open at-need calls needing a director), Inbox (unhandled threads). Counts are
live and honest — a real count from the data,
0shown as no badge (never a fabricated number; §6).
Should NOT see:
- A rail item that routes nowhere / a "coming soon" dead link (every rail item resolves to a real screen; setup bodies render even if unstyled on day 1).
- A badge count that does not reflect real rows.
2.3 — Top bar
Should see:
- The current-screen title (+ a brief date/greeting), a setup-progress pill
(e.g. "Setup 60%") that reflects the tenant's real onboarding completeness and
deep-links into the setup flow, a Grace launcher ("Ask Grace") that opens the
concierge panel (behavior →
grace-onboarding-concierge-mvp.md), a notification bell, and a mobile hamburger (<lg). - A search/Cmd+K affordance is display-only (honest — it is not wired; realtor posture). It must be reported as not-yet-functional, not presented as working.
Should NOT see:
- A setup pill showing a fabricated/hardcoded percentage (it must derive from real setup state, or be omitted).
- A notification bell that claims unread counts it cannot substantiate.
2.4 — Rail collapse + mobile drawer (behavior, not DOM)
Should see (verify at ≥2 timepoints — feedback_verify_behavior_not_dom_presence):
- Clicking collapse shrinks the rail to the icon rail and the content area reflows wider; the choice persists across reloads.
- Under
lg, the rail is an off-canvas drawer: the hamburger opens it, a backdrop tap or Esc closes it, focus is trapped while open and restored on close. Same IA as desktop (no web/mobile divergence — anti-pattern #5).
Should NOT see:
- A hydration flash (rail momentarily expanded then collapsing) — the pattern must read the persisted state before first paint.
- A mobile drawer that leaves the page scrollable behind it or loses focus.
3. Screen archetype — Home (bento, already shipped, kept)
Status: SHIPPED (the "Grace bento" Home body,
FuneralDashboardBody.tsx). This spec keeps it and only asks that it now render
inside the new frame and honor the token mapping (§1).
Should see:
- A bento command center: a hero tile (the one daily number — e.g. today's at-need calls) top-left at ~2× scale, then ≤10 tiles sized by importance (recent activity, calls this week, callbacks pending, setup progress, a Grace entry tile).
- Metrics use
tabular-nums; the hero tile is the largest. - Honest empty/onboarding state when there is no activity yet (§6).
Should NOT see:
- A uniform card wall (anti-pattern #2) or a fabricated KPI (§6).
4. Screen archetype — Inbox (3-pane)
Mirrors the realtor Conversation Inbox archetype (realtor-inbox-mvp.md), funeral
terminology.
Should see:
- A 3-pane split: Pane 1 — filters / saved views (At-Need · Pre-Planning · Family Contact · Crisis · General · Callback · All, with live counts) + channel filter (Voice / Chat); Pane 2 — the thread list (caller name, relationship-to-deceased snippet, timestamp, a type chip + urgency badge where applicable, AI-handled rows muted with an "AI" badge); Pane 3 — the thread + a right context rail (AI call summary, caller facts, "flag for director" / take over actions).
- Thread bubbles label AI vs human vs caller; an escalation appears as a system row that states why the agent handed off ("wants to arrange a service today — waiting for a director"), never a bluffed answer (AI Bridge).
- Nothing auto-sends; a human presses any reply (no unattended sends).
Should NOT see:
- Church filters (prayer request, care/broadcast, denomination) — funeral taxonomy
only (matches
funeralwiseai-vertical.md§6 banned terms). - A blank 3-pane (demo-seeded preview + onboarding nudge — §6), or a spinner (skeleton rows/thread instead).
5. Screen archetypes — At-Need Calls & Pre-Planning (list + drawer)
5.1 — At-Need Calls (list + right drawer, crisis-urgency treatment)
Should see:
- A list of at-need calls (family calling within hours/days of a death), each row: caller name, relationship, time, and an urgency indicator that is text + color, never color alone (e.g. an "URGENT — family in crisis" label with a copper left-edge accent; copper is the highlight edge, the word "URGENT" is the readable status — copper is not the label text, §1).
- A right drawer on row-open: the transcript + an AI summary header, caller contact, nature of call, and a "director takes over" action. Opening the drawer does not lose the list (two-tier: list triage + drawer detail, no tab-sprawl).
- A genuine crisis disclosure (self-harm / acute distress) surfaces the
universal 988 pathway and a "connect to a director" offer — never handled as a
routine lead (AI Bridge +
funeralwiseai-vertical.md§8).
Should NOT see:
- Urgency shown by color only (AODA), or a "family in crisis" row that reads the same as a routine callback.
- The AI presented as having counselled the family or promised confidentiality.
5.2 — Pre-Planning (list + drawer, next-action emphasis)
Should see:
- A list of pre-planning inquiries + a right drawer with the inquiry detail, contact, and a clear next action (schedule a consult / flag for director). Next-action is emphasized (the realtor "next-best-action" pattern), calm and unhurried in tone (this is planning, not crisis).
Should NOT see:
- The same crisis-urgency treatment as At-Need (pre-planning is not time-critical); a fabricated inquiry (§6).
6. Honest-metrics & empty-state contract (hard gate)
Every tile, list, badge, and pill on every screen obeys the portfolio "honest metrics, never theater" rule (CLAUDE.md) + realtor anti-pattern #6:
| Situation | Expected output |
|---|---|
| No calls / no activity yet | A demo-seeded preview + one CTA + onboarding nudge ("Your families' calls will appear here once your line is live") — never a blank panel. |
| A metric with no real data | Show an honest empty/zero state or omit the tile — never a fabricated number (no "Delivered/Opened" theater; no invented call counts). |
| A badge count of 0 | No badge (not a "0" that implies activity). A badge shows only a real actionable count. |
| Setup pill | Reflects the tenant's real onboarding completeness; if it can't be computed, omit it — never hardcode a percentage. |
| Loading | Skeletons, not spinners (anti-pattern #7). |
| Error | Inline "Couldn't load — retry"; the rest of the shell stays usable. |
| Not-provisioned (no line connected) | Honest "AI front office not yet connected — calls not measured yet," not fabricated conversations. |
Demo content is seeded (the Moelker Family Funeral Home demo tenant, plan Phase 0) and clearly demo — it never masquerades as a real family, and nothing in this UI path may email anyone (plan §5 safety rail).
7. Church / vet regression gate (funeral-gated — pixel-unchanged)
Because AdminShell / AdminDashboard are shared, the single most important
regression guard:
- CH-1 — A church demo admin token renders the existing church tab shell, visually unchanged (no funeral rail, no slate chrome, no sage/copper). Verify with a click-through on a church demo token.
- CH-2 — A vet admin token renders the existing vet shell, unchanged.
- CH-3 — The FuneralShell code path is entered only for
verticalProfile.key === 'funeral'; the church/vet branch is provably untouched (diff shows the funeral frame is additive/gated, not a rewrite of the shared shell). - CH-4 — Check
knowledge/tests/registry.yamlforadmin/[token]critical-path overlap before touching shared files; a shared-shell change climbs the verification ladder (deployed-URL Playwright, not build-passes).
8. ?tab= deep-link compatibility
- DL-1 — Existing deep links (
funeralwiseai.com/admin/[token]?tab=inbox,?tab=training, etc.) still resolve to the correct screen under the new rail (the rail destinations map onto the same tab keys; the param is not broken). - DL-2 — Selecting a rail destination updates the URL/tab state so a deep link can be copied/shared (parity with the pre-rail behavior).
- DL-3 — An unknown
?tab=value falls back to Home (no crash), matching the pre-existing behavior.
9. Accessibility (AODA → WCAG 2.1 AA)
- Landmarks: rail =
nav; top bar =banner/header; content =main; the 3-pane inbox panes are navigable regions; lists expose selectable rows. - Status never color-only: arrangement stages, urgency ("URGENT — family in crisis"), AI-vs-human authorship, and badge meaning are conveyed by text + icon, not color alone. Copper is a highlight edge, never the readable status.
- Contrast: ≥4.5:1 for all text; the sage action =
#3F685C(~6.3:1 on white); copper text-on-tint uses#8A4A0Bon#FEF3E2(verify ≥4.5:1); slate chrome text meets AA. - Keyboard: rail collapse, drawer open/close (Esc), destination nav, row → drawer, take-over/flag, and the Grace launcher are all keyboard-operable with visible focus; the mobile drawer traps + restores focus.
- Motion: rail/drawer transitions and skeletons respect
prefers-reduced-motion. - Screen-reader: the setup pill, badge counts, and notification bell expose their meaning as text (e.g. "Setup 60% complete", "3 at-need calls need a director").
10. Acceptance checklist (QA runs on the deployed URL)
Behavioral verification on https://funeralwiseai.com (the real host — the
host-alias middleware is skipped on preview hosts, plan §Verification) against the
Moelker demo token; never "build passes". Sample at ≥2 timepoints; assert
change. Any voice-path touch additionally requires voice-agent-engineer review.
// Shell + gating
test.fixme('funeral admin renders the slate left-rail shell + top bar (setup pill, Ask Grace, bell)', () => {});
test.fixme('rail collapses to a 64px icon rail and the choice persists across reload (no hydration flash)', () => {});
test.fixme('under lg the rail is an off-canvas drawer: hamburger opens, Esc/backdrop closes, focus trapped+restored', () => {});
test.fixme('At-Need and Inbox rail badges show REAL counts; a 0 shows no badge (no fabricated number)', () => {});
// Token mapping / a11y
test.fixme('primary action color is sage #3F685C measuring >=4.5:1 on white; copper is edge/badge only, never text', () => {});
test.fixme('arrangement stages + at-need urgency are text+color, never color alone (AODA)', () => {});
// Screens
test.fixme('Home renders the bento inside the new frame (hero tile 2x, <=10 tiles, tabular-nums, honest empty state)', () => {});
test.fixme('Inbox is a 3-pane split with funeral filters (At-Need/Pre-Planning/Family Contact/Crisis/General/Callback)', () => {});
test.fixme('At-Need is list + right drawer; a crisis row shows URGENT text + copper edge + transcript/AI summary + director take-over', () => {});
test.fixme('Pre-Planning is list + drawer with next-action emphasis (calm, not crisis-styled)', () => {});
// Honesty + deep links + regression
test.fixme('every tile/list/badge shows a demo-seeded/honest empty state — never blank, never a fabricated metric', () => {});
test.fixme('?tab=inbox (and other existing deep links) still resolve under the new rail', () => {});
test.fixme('a church demo token renders the existing church tab shell PIXEL-UNCHANGED (no funeral rail/slate/sage)', () => {});
test.fixme('a vet admin token renders the existing vet shell unchanged', () => {});
11. Open items (Stage-2 founder validation)
| # | Question | Blocks | Priority |
|---|---|---|---|
| 1 | Final rail IA — exact rail destinations + order + labels (the 8 tabs promoted; is "At-Need Calls" its own destination or an Inbox filter?). | §2.2, §4, §5 | P0 |
| 2 | Badge-count sources — which tables/queries back the At-Need + Inbox badges (verify exact table/column names via information_schema, Rule #18). | §2.2, §6 | P0 |
| 3 | Setup-progress computation — what defines the funeral tenant's onboarding % (which setup steps count)? | §2.3, §6 | P0 |
| 4 | Inbox filter set — confirm the exact funeral filter chips (At-Need / Pre-Planning / Family Contact / Crisis / General / Callback / All). | §4 | P1 |
| 5 | Arrangement stages — are the 5 stages (New inquiry / Arrangements / Service set / Completed / Aftercare) correct, and where do they surface (a pipeline screen, or a status field on inbox rows)? | §1, §5 | P1 |
| 6 | Notification bell — is there a real notification feed to back it, or is it display-only for now (must be honest either way)? | §2.3 | P1 |
| 7 | Compact/density toggle — carry the realtor compact toggle into funeral, or comfortable-only at launch? | §1 | P2 |
12. Guardrails for agents building against this spec
- Rule #17 not satisfied — do not build the shell/screens until founder approval.
- Funeral-gated, church/vet pixel-unchanged — the shared
AdminShell/AdminDashboardchange is additive + gated; §7 is a hard regression gate (verify a church demo token on the deployed URL). - Honest metrics, never theater — no fabricated counts, no hardcoded setup %, no blank panels (demo-seeded states).
- AI Bridge — AI-authored surfaces are labelled AI, "flag for director" is always present, no confidentiality promises, crisis surfaces 988 + a human.
- Build-to-spec port, not component reuse — port the realtor shell with funeral tokens; do not import realtor components into the funeral path.
- Verify on the real host (
funeralwiseai.com), behavior not DOM presence, reduced-motion + mobile emulation, evidence attached (CLAUDE.md ladder). - Nothing in this path may email anyone (plan §5); nothing references Kolssak.
- If code diverges, update this spec first (founder approval), then the code.
End of spec. STATUS: DRAFT — NOT APPROVED.