h.work — Client × AI Specialist Interaction
Version: 2026-06-08 | Scope: WebChat · Slack Channel · Expert Dashboard
1. Architecture Overview
Client (org_admin / org_member)
├── WebChat (/client/chat)
└── Slack Channel (DM or @mention in a channel)
↓
API (NestJS) — unified message entry point
↓
Agent Service (Python) — AI-generated reply
↓
Expert Dashboard (/workspace/queue)
├── Expert reviews AI draft
└── Approves / edits → reply sent via original channel
Core Principles
- Messages from all channels are unified in the Expert Dashboard — no cross-channel mixing
- Default HITL (Human-in-the-Loop):
autoRespondThreshold = 101— every AI reply must be approved by an Expert before delivery - Message dedup + idempotent writes to prevent double-processing
2. WebChat Module
2.1 Feature Description
After logging in, a Client navigates to /client/chat and can:
- View all Specialists assigned to the org (left-side profile panel)
- Start a new Thread with any Specialist
- Maintain multiple independent Threads per Specialist
- Send text, images, and file attachments
- Receive AI Specialist replies in real time (Socket.io, 8-second polling fallback)
2.2 Current Implementation Status
| Feature | Status |
|---|---|
| Specialist selection + profile display | ✅ Done |
| New Thread / Thread list (left sidebar) | ✅ Done |
| Send message (text) | ✅ Done |
| Message attachments (images / files) | ✅ Done |
| Socket.io real-time reply delivery | ✅ Done |
| Thread rename / Resolve / Reopen | ✅ Done |
| Unread message badge | ✅ Done |
| Thread list sorted by latest message (descending) | ✅ Done |
| Thread status display (in_progress / awaiting_client / resolved) | ✅ Done |
| Channel source indicator (Slack / Email / WhatsApp icon) | ✅ Done |
2.3 Message Flow
Client types message → POST /conversations/:id/messages
↓ Returns immediately (201)
Background async: runAgentPipeline()
→ Python Agent generates reply (with confidence / risk_level)
→ Default: enters Expert Queue (threshold = 101)
→ Expert approves → Socket.io pushes reply to Client
2.4 Open Items
| # | Issue | Priority |
|---|---|---|
| W-1 | When creating a new Thread in an org with multiple Specialists, the user must explicitly select which Specialist to address | High |
| W-2 | WebChat's own channel icon is missing in the Thread list (only external-channel icons like email/slack are shown) | Medium |
| W-3 | Client-side message status feedback is missing (sending / delivered / expert reviewing) | Medium |
| W-4 | Attachment preview: inline image preview works; PDF/file shows only a chip with no content preview | Low |
3. Slack Channel Module
3.1 Design Goal
Each AI Specialist maps to a dedicated Slack App. Once a Client org installs the App into their Slack Workspace, org members can communicate with that Specialist via:
- DM: Direct message the Slack App
- Channel @mention: @mention the App in any channel
- Bound Channel (opt-in): A channel explicitly bound to a Specialist routes all messages to that Specialist — no @mention required
3.2 Slack App Activation Flow
SuperAdmin / AM generates a Slack App Manifest for a Specialist in the Ops dashboard
↓
Create the App in the Slack API Console using the Manifest
(includes bot scopes / Events API / OAuth settings)
↓
Configure the App's Client ID / Client Secret / Signing Secret in Humanwork
↓
Specialist ↔ Slack App association stored in IntegrationCredential (encrypted)
↓
Client org admin clicks "Add to Slack" → OAuth 2.0 authorization flow
↓
oauth.v2.access → bot_token stored in IntegrationCredential (org-scoped)
↓
Activation complete: org members can DM or @mention the Specialist via Slack
3.3 Message Trigger Strategy (Three Layers, per PRD)
Layer 1 — Hard Triggers (always respond)
- DM sent directly to the Slack App
- @mention of the bot in any channel
- Follow-up messages in a Thread the bot has already participated in (Thread continuation)
Layer 2 — Contextual Scoring (intelligent decision, Phase 2)
composite_score = channel_weight × (keyword_match + question_intent)
score > threshold_high → respond immediately
score > threshold_low → enter Expert review queue
else → silent storage
Layer 3 — Implicit Relevance (semantic vector matching, Phase 3)
- Embedding-based similarity matching
- Requires cold-start labeled data
3.4 Message Processing Flow
Slack Events API → POST /channels/slack/events
↓ HMAC-SHA256 signature verification → return 200 immediately
↓
BullMQ channels-inbound queue (async)
↓
handleRuntimeEvent()
├── team_id → IntegrationCredential → resolve orgId
├── Dedup: event_ts / event_id already processed? → skip
├── Classify trigger: DM / @mention / Bound Channel / Thread continuation
│ └── No trigger → silent ingest into channel_context_messages
↓
findOrCreateConversation() (Slack user → customer mapping)
↓
conversationsService.sendMessage() [synchronous — waits for Agent]
↓
Agent generates reply → Expert Queue
↓
Expert approves → BullMQ channels-outbound
↓
slack.chat.postMessage(channel, text, thread_ts) → reply in original thread
3.5 Current Implementation Status
| Feature | Status |
|---|---|
| Slack App Manifest generation (SlackManifestSuggester) | ✅ Done |
| OAuth 2.0 install flow (Add to Slack) | ✅ Done |
| HMAC-SHA256 signature verification | ✅ Done |
| DM message ingestion + routing | ✅ Done |
| @mention message ingestion + routing | ✅ Done |
| Thread continuation tracking (slack_engaged_threads) | ✅ Done |
| Bound Channel (opt-in, no @mention required) | ✅ Done |
| Message dedup (Redis NX + event_ts) | ✅ Done |
| Silent ingest (channel_context_messages) | ✅ Done |
| Expert reply dispatched back to Slack thread | ✅ Done |
| Per-Specialist independent Slack App | ⚠️ Architecture supports it, but OAuth currently uses shared global SLACK_CLIENT_ID |
| Layer 2 Contextual Scoring | 🔲 Not implemented (Phase 2) |
| Layer 3 Semantic vector matching | 🔲 Not implemented (Phase 3) |
3.6 Open Items
| # | Issue | Priority |
|---|---|---|
| S-1 | Per-Specialist Slack App: current OAuth uses a global SLACK_CLIENT_ID/SECRET; need per-Specialist App credentials and independent install flow | High |
| S-2 | Client-facing Slack connection management UI: show connected workspace, bound channels, disconnect / reconnect actions | High |
| S-3 | Bound Channel self-service UI (currently back-end only; Client Admin should be able to bind channels themselves) | Medium |
| S-4 | Layer 2 Contextual Scoring engine implementation | Medium |
| S-5 | Slack message image/file attachments forwarded to Expert Dashboard | Medium |
| S-6 | Expert replies support Slack mrkdwn rich-text formatting | Low |
4. Expert Dashboard Module
4.1 Feature Description
After logging in, an Expert navigates to /workspace/queue and can:
- View all conversations pending review (from WebChat + Slack + Email + WhatsApp)
- Read the AI-generated draft reply (with confidence score + risk level badge)
- Edit the draft and confirm to send (reply delivered via the original channel)
- Browse the full conversation history (Client messages + AI drafts + past Expert replies)
- Sort conversations across multiple dimensions (risk level / created time / SLA)
4.2 Current Implementation Status
| Feature | Status |
|---|---|
| Queue list (pending conversations) | ✅ Done |
| AI draft display + confidence score | ✅ Done |
| Risk level badge (low / medium / high / critical) | ✅ Done |
| Expert edit draft + confirm send | ✅ Done |
| Reply dispatched via original channel (webchat / slack / email) | ✅ Done |
| Full conversation history (complete message list) | ✅ Done |
| Channel source badge (email / slack etc.) | ✅ Done (email has badge; other channels incomplete) |
| SLA timer | ✅ Done |
| Keyboard shortcuts | ✅ Done |
| Socket.io real-time new message notification | ✅ Done |
| Unread message badge | ✅ Done |
| Queue sorting (risk / time / SLA) | ✅ Done |
| Message attachment display (images / files) | ✅ Done (WebChat attachments; Slack attachment forwarding pending) |
| Multi-Specialist filter view | ⚠️ Filtered by expert_access permissions; no UI toggle |
| Per-conversation unread count | ⚠️ Global unreadCount exists; per-conversation count needs verification |
| Conversation status management | ⚠️ Status values exist (pending / awaiting_client / resolved); transition rules need to be defined |
4.3 Open Items
| # | Issue | Priority |
|---|---|---|
| E-1 | Unified channel source badge: all channels (webchat / slack / whatsapp / email) display their source badge in both the Queue list and conversation detail | High |
| E-2 | Slack attachment forwarding: images and files from Slack messages must be forwarded and viewable in the Expert Dashboard | High |
| E-3 | Conversation status definition: formally define pending / open / awaiting_client / resolved — meanings, transition conditions, and UI display rules | High |
| E-4 | Per-Specialist filter view: when an Expert has access to multiple Specialists, support filtering conversations by Specialist | Medium |
| E-5 | Per-conversation unread count: show exact unread message count per Thread in the conversation list | Medium |
| E-6 | Rich-text message rendering: Slack mrkdwn, Markdown, link previews | Medium |
| E-7 | Internal Note: Expert-to-Expert internal messages (API exists; UI completeness to be verified) | Low |
5. Task Summary
Module 1 — Expert Dashboard
| Task ID | Description | Priority | Depends On |
|---|---|---|---|
| E-1 | Unified channel source badge in Queue list for all channels | P0 | — |
| E-2 | Slack attachment forwarding: storage + display in Expert Dashboard | P0 | S-5 |
| E-3 | Define and implement conversation status transition rules + UI labels | P0 | — |
| E-4 | Per-Specialist filter view | P1 | — |
| E-5 | Per-conversation precise unread message count | P1 | — |
| E-6 | Rich-text message rendering (Markdown + Slack mrkdwn) | P2 | — |
| E-7 | Internal Note UI completeness audit | P2 | — |
Module 2 — Slack Channel
| Task ID | Description | Priority | Depends On |
|---|---|---|---|
| S-1 | Per-Specialist independent Slack App credentials (separate Client ID / Secret / Signing Secret per Specialist) | P0 | — |
| S-2 | Client Admin Slack connection management UI (connected workspaces / disconnect / reconnect) | P0 | S-1 |
| S-3 | Bound Channel self-service binding UI for Client Admin | P1 | S-2 |
| S-4 | Layer 2 Contextual Scoring engine (PRD Phase 2) | P1 | — |
| S-5 | Slack message image/file attachment forwarding to Expert Dashboard | P1 | — |
| S-6 | Expert reply supports Slack mrkdwn formatting | P2 | E-6 |
Module 3 — WebChat
| Task ID | Description | Priority | Depends On |
|---|---|---|---|
| W-1 | Support Specialist selection when creating a new Thread (multi-Specialist org scenario) | P0 | — |
| W-2 | Client-side message send status feedback (sending / delivered / expert reviewing) | P1 | — |
| W-3 | WebChat channel icon in Thread list (channel dot) | P2 | — |
| W-4 | PDF / document attachment preview (currently shows chip only, no content preview) | P2 | — |
6. Key Data Models
| Entity | Description |
|---|---|
conversations | Each Thread — contains channel / status / agentName / orgId |
messages | Message records — role (user / ai / expert) / attachments / heldForReview |
expert_queue_items | AI draft pending review; Expert acts on this |
integration_credentials | Slack / Email / WhatsApp tokens — encrypted, org-scoped |
slack_channel_bindings | Channel opt-in binding: channel_id → Specialist |
slack_engaged_threads | Thread continuation tracking (sliding-window engaged_at TTL) |
channel_context_messages | Silent ingest context store (FTS index) |
expert_access | Expert ↔ (Org × Specialist) access grant (ADR-007) |
7. Recommended Starting Points
Based on impact and dependency order, suggested first tasks:
- S-1 — Per-Specialist Slack App credentials (unlocks the full multi-Specialist Slack story)
- E-1 — Unified channel source badge (low effort, high visibility)
- E-3 — Conversation status definition (blocking several downstream UI decisions)