Correspondence

Project-linked conversations, dissolved into the places where you already work.

The Design Principle

Every conversation in the portal is linked to a project. No exceptions. General questions that don’t belong to a project belong on a phone call or a text message — not in the system.

This constraint keeps the conversation log useful: when you open a project six months from now, you see every discussion that happened about it, in the same place as the certs, photos, and invoices.

There is no standalone Messages page. A separate Messages page gathers information in a place where context doesn’t exist. The project detail page already has the address, the permit, the SOW, the timeline, the files. Threading conversations alongside that context eliminates the need to cross-reference.

Where Conversations Live

SurfaceWhat You See
Project DetailFull conversation thread with compose area, friday-ai access, file upload
Projects tableRed dot on the project ID when there are unread messages
CalendarRed dot on the inspection event when the linked project has unread messages
Morning BriefingUnread counts in the Messages stat row
Notification BellUnread message count in the bell dropdown
Sidebar SearchConversations appear in quick-search results

Red Dot System

Red is the only accent color. A 6px red circle on a project row or calendar event means: someone said something you haven’t read yet. It disappears when you open the conversation. No numbered badges, no colored pills — just presence or absence.

On calendar events, RFI dots take precedence over message dots.

The Thread

The Correspondence section on a project detail page shows one thread per conversation. Multiple conversations (general thread + RFI) show as tabs.

Visual Language

ElementTreatment
Sender + timestampMonospace, uppercase, light — metadata recedes
Message bodySerif, normal weight — prose stands forward
friday-ai responsesCream background, gold left border, dot-matrix attribution
Date breaks1px dashed rule with centered date
ActionsLowercase monospace text links in a bottom bar
RFI conversationsRed left border

Conversation Types

TypeWhen to UseVisual Cue
ProjectGeneral discussion about the jobNeutral left border
RFIRequest for information — something unclear or missingRed left border, RFI badge
ReviewCert or invoice review discussionNeutral, linked to OwnerReviewQueue

No direct messages allowed. The backend rejects project_id: null. If it doesn’t belong to a project, it doesn’t belong in the portal.

Composing & Sending

ActionHow
Send a messageEnter or click the send arrow
New lineShift + Enter
Ask friday-aiClick ask friday in the action bar
Attach a fileClick attach in the action bar
Flag as RFIClick flag rfi — converts the conversation type

Messages are sent via REST and broadcast to all viewers via WebSocket (optimistic render).

Real-Time Features

FeatureBehavior
Typing indicators”Jacob is typing…” — auto-clears after 4 seconds
Viewing presenceThread header shows who else is viewing
Read receiptsOpening a conversation marks all messages as read
Live deliveryNew messages appear for all viewers without refresh
ReconnectionAuto-reconnects with 3-second backoff

Ask Friday (In-Thread)

The ask friday action invokes friday-ai directly inside the conversation thread. Friday reads the entire message history, understands the linked project, and responds as a visible participant.

Ask Friday (in thread)Friday Bubble (floating icon)
WhereInside project CorrespondenceFloating eye icon, every page
AudienceTeam-visiblePrivate
StoragePermanent (SQLite)Session only
ProtocolWebSocket (live broadcast)REST (request/response)
ContextFull conversation + project dataCurrent page context

Notification Routing

ChannelWhen
PortalAlways — in-app notification + red dot
EmailIf recipient offline, delayed 2 minutes (cancellable)
WhatsAppField team (Darius) — critical/time-sensitive only
SMSIf enabled in preferences — critical only

DND hours respected. Consolidation batches multiple notifications within a 2-minute window.

Storage & Endpoints

WhatWhere
Conversations & messages/data/portal-auth/chat.db
WebSocket endpoint/portal/chat/ws?token={jwt}
REST base/portal/chat/*
Unread by project/portal/chat/unread-by-project
friday-ai context/portal/friday/chat-context
Project timeline/portal/chat/project-timeline/{id}