Skip to main content

Acceptance Criteria — Client Portal Chat

Status: DRAFT Date: 2026-05-01 Author: Tyler AI review / E alignment Linked Stories: C-W1, C-W2, C-W3, C-W4, C-W8


Context: What a Client Actually Sees

When a client logs in to [slug].human.work, they land on /portal/chat. This is their entire experience of the product. They are talking to "Amy" — their named AI + expert specialist. They never see queue items, risk scores, or expert names.

The client's mental model: "I have a dedicated support specialist named Amy at Human.work. I message her when I need something done."


Current Issues (as of 2026-05-01)

#IssueSeverity
1No org slug shown in the top-left title — hardcoded "H" / "human.work"High
2"Message" button on the Workforce pane does not start a thread — nothing happensHigh
3Chat interface not reachable without knowing the URL — no clear entry pointHigh
4No Specialist assigned or shown — client doesn't know who Amy isHigh
5Section label says "Direct Messages" — should be "Your Threads"Medium
6Section label says "Your Experts" — should be "Your Specialists"Medium

Acceptance Criteria

AC-PC-01 — Org slug shown in sidebar header

Given a client is logged into their portal, When the chat UI loads, Then the top-left sidebar header shows the org's slug (e.g. acmecorp) as the workspace name, not a hardcoded string.

Implementation: fetch slug from JWT payload → orgMemberships[0]orgSlug, or from a /portal/context endpoint. Fall back to "human.work" if unavailable.

Acceptance check: Log in as any seeded client user. Top-left shows their org's slug, not "H / human.work".


AC-PC-02 — Specialist name and avatar shown in sidebar and chat header

Given an AM has assigned a Specialist to the client's org, When the client opens the chat, Then the Specialist's first name (e.g. "Amy") and avatar (initials fallback) appear:

  • In the sidebar thread list (as the "contact" name)
  • In the chat header when a thread is selected
  • In the Workforce pane card

Given no Specialist is assigned, Then the name falls back to "Amy" (the generic persona) and the avatar shows "A".

Implementation: call GET /portal/context or embed specialist info in the JWT; the API side should return { specialistName, specialistAvatarUrl, orgSlug, agentName }.


AC-PC-03 — "Message" button on the Workforce pane starts a new thread

Given the client is on the chat page with no thread selected (empty state / Workforce pane visible), When the client clicks the "Message" button on the Specialist card, Then:

  • A new conversation is created via POST /conversations
  • The sidebar updates to show the new thread
  • The chat panel opens on the new thread, ready to type

Acceptance check: Click "Message" on the Specialist card → thread appears in sidebar → user can type and send immediately.


AC-PC-04 — Client can enter chat and send a message

Given the client is in a thread (selected in sidebar or newly created), When the client types in the message input and presses Enter or the send button, Then the message is sent via POST /conversations/:id/messages and appears immediately in the message list (optimistic update).

And then within 3 seconds (polling), Amy's reply appears if available.

Acceptance check: Full send→receive loop works end-to-end on dev.


AC-PC-05 — Sidebar section labels use correct terminology

Given the client is viewing the chat sidebar, Then:

  • The thread list section is labelled "Your Threads" (not "Direct Messages")
  • The specialist section (if present) is labelled "Your Specialists" (not "Your Experts")

AC-PC-06 — Sidebar "New Thread" button creates a conversation

Given the client already has one or more threads, When the client clicks "New Thread" (or "+ New Task"), Then a new conversation is created and selected, matching AC-PC-03 behavior.


AC-PC-07 — Thread list shows correct thread metadata

Given the client has existing threads, When the sidebar renders, Then each thread entry shows:

  • Specialist name (or "Amy" fallback)
  • A snippet of the last message (truncated at ~40 chars)
  • Relative timestamp of last activity
  • Active indicator (green dot) if the thread status is active or open

AC-PC-08 — Chat header shows correct specialist and thread info

Given a thread is selected, When the chat panel header renders, Then it shows:

  • Specialist name and avatar
  • Online/active status ("Online · Support Specialist" or "Conversation resolved")
  • Active/Resolved status badge

AC-PC-09 — Empty state leads to first thread naturally

Given the client has no existing threads, When the chat page loads, Then the Workforce pane shows the Specialist card with a "Message" button. When the client clicks "Message", Then a thread is created and the client lands directly in the chat input, ready to type.

There is no dead-end state — the client always has a clear call to action.


AC-PC-10 — Mobile-responsive layout

Given the client accesses the portal on a screen < 640px wide, When no thread is selected, the sidebar is shown full-width. When a thread is selected, the chat panel fills the screen and the sidebar is hidden. A "back" button or swipe/tap gesture returns to the sidebar.


AC-PC-11 — Last-viewed thread is remembered per user (localStorage)

Given the client previously had a thread selected, When the client returns to /portal/chat, Then the last-viewed thread is restored automatically (keyed by userId in localStorage).

Key format: hw_last_thread_{userId}`` → { conversationId, timestamp }.


AC-PC-12 — Thread title editable inline (optional, P2)

Given a thread is selected, When the client clicks/double-clicks the thread title in the sidebar or header, Then an inline edit field appears. When the client types a new name and presses Enter, Then the thread title is saved (locally or via PATCH /conversations/:id).


Out of Scope (for this spec)

  • Expert-facing queue or escalation UI (separate spec)
  • WhatsApp channel integration (deferred — see WHATSAPP.md)
  • Client approval flow for high-stakes actions (UI scaffolded as ApprovalBanner but API not wired — P0 gap tracked in USER_STORIES.md C-W8)
  • Conversation search (C-W6, P1 — future sprint)

Test Credentials (Dev)

RoleEmailPassword
Client userSee README.md seeded credentialsSee README

Login flow: /login → invite OTP → /portal/chat


Implementation Notes

API surface needed

The chat portal needs the following data that is currently not returned to the frontend:

  1. GET /portal/context (new or augment existing /portal/trial-info) — returns:

    {
    "orgSlug": "acmecorp",
    "orgName": "Acme Corp",
    "specialistName": "Amy",
    "specialistFirstName": "Amy",
    "specialistAvatarUrl": null,
    "agentName": "Amy"
    }
  2. POST /conversations must work for role: "customer" — currently implemented, verify on dev.

  3. POST /conversations/:id/messages must return the sent message — currently implemented.

Frontend changes needed

ComponentChange
PortalChat.tsx / SidebarReplace hardcoded "H" / "human.work" with org slug from context
PortalChat.tsx / SidebarReplace "Direct Messages" with "Your Threads"
PortalChat.tsx / WorkforceEmptyState"Message" button wired to handleNew() (check if broken)
PortalChat.tsx / SidebarShow specialist name/avatar instead of hardcoded "Amy"/"A"
PortalChat.tsxLoad portal context on mount, use for all specialist references
PortalChat.tsxImplement last-viewed thread via localStorage
MobileAdd responsive breakpoint — hide sidebar on narrow screens when thread selected

Sign-off Required

  • E — product alignment
  • Tyler — implementation assigned
  • QA — test pass on dev before merge to staging