Unlikely Professionals

Correspondence

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

There is no Messages page. Conversations live inside each project, alongside the files, site visits, certifications, and invoices they refer to. A red dot on a project row means someone is talking. Open the project to read and reply.

01 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.

Why 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.
02 Where Conversations Live

Open any project. Scroll to the Correspondence section. That's where the conversations are — inline, not behind a link.

SurfaceWhat You See
Project DetailFull conversation thread with compose area, Friday access, file upload
Projects table Red dot on the project ID when there are unread messages
Calendar Red dot on the inspection event when the linked project has unread messages
Morning BriefingUnread counts in the Messages stat row — click through to Projects
Notification BellUnread message count in the bell dropdown — click through to Projects
Sidebar SearchConversations appear in quick-search results — click to open the linked project
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.
03 The Thread

The Correspondence section on a project detail page shows one thread per conversation. If a project has multiple conversations (a general thread and an RFI, for instance), tabs appear across the top.

jacob · mar 21 · 9:14 am
Permit for this one is expiring next week. Should I push the inspection up?
dustin · mar 21 · 9:22 am
Yes, move it to Wednesday if the crew is available.
··· friday ··· · mar 21 · 9:23 am
Darius is available Wednesday. The permit expires March 28. I've moved the visit to March 26.
reply ask friday attach flag rfi

Visual Language

ElementTreatment
Sender + timestampMonospace, uppercase, light — metadata recedes
Message bodySerif, normal weight — the prose stands forward
Friday responsesCream background, gold left border, ···friday··· dot-matrix attribution
Date breaks1px dashed rule with centered date — no heavy dividers
ActionsLowercase monospace text links in a bottom bar — no buttons
RFI conversationsRed left border instead of the default cream/neutral
04 Starting a Conversation

If a project has no conversation yet, the Correspondence section shows a compose area. Type a message and send — a conversation is auto-created and linked to the project.

You can also start a conversation by clicking + new message in the action bar, which opens a compose area with a type selector (general, RFI, or review).

Open a project — Correspondence section
Type your message in the compose area
Conversation auto-created, linked to this project
All future messages thread here

Conversation Types

TypeWhen to UseVisual Cue
ProjectGeneral discussion about the jobNeutral left border
RFIRequest for information — something is unclear or missingRed left border, RFI badge
ReviewCert or invoice review discussionNeutral, linked to review queue
No Direct Messages The system does not allow conversations without a project link. The backend rejects project_id: null. This is by design: if it doesn't belong to a project, it doesn't belong in the portal.
05 Composing & Sending
ActionHow
Send a messageEnter or click the send arrow
New lineShift + Enter
Ask FridayClick 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 then broadcast to all viewers via WebSocket. You see your message immediately (optimistic render) while the server confirms delivery.

06 Real-Time Features

A persistent WebSocket connection keeps threads live. When someone else sends a message to the conversation you're viewing, it appears instantly.

FeatureBehavior
Typing indicators"Jacob is typing..." appears below the thread. Auto-clears after 4 seconds.
Viewing presenceThread header shows who else is viewing the same conversation.
Read receiptsOpening a conversation marks all messages as read. The red dot on the project row clears.
Live deliveryNew messages appear for all viewers without refresh.
ReconnectionAuto-reconnects with 3-second backoff if the connection drops.
07 Ask Friday

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

Type a question in the compose area
Click "ask friday"
Your question saves as a message, then Friday reads the full context
Friday's reply appears for everyone viewing the thread

Friday's response is saved permanently and broadcast via WebSocket. Friday messages are visually distinct: cream background, gold left border, dot-matrix attribution (···friday···).

Tool Access Friday in the thread has access to its full tool set: project lookups, schedule queries, pipeline status, pricing intelligence, SQL queries, write actions (modify schedule, send certs, WhatsApp), and chart rendering. Tool access varies by role — owner/admin get all intelligence tiers; staff get queries and charts; client-facing contexts get safe lookups only.

Friday in Thread vs. Friday Bubble

Ask Friday (in thread)Friday Bubble (floating icon)
WhereInside a project's Correspondence sectionFloating eye icon, available on every page
AudienceTeam-visible — everyone sees the replyPrivate — only you see it
StoragePermanent (SQLite, part of the project record)Session only (lost on tab close)
ProtocolWebSocket (live broadcast)REST (request/response)
ContextReads full conversation history + project dataReads current page context

Same AI, different audiences. Friday in the thread is a team participant. The bubble is a private assistant.

08 Unread Indicators

The system tracks read state per user per conversation. Unread messages surface as red dots across multiple views, so you don't need to check each project individually.

WhereIndicatorClears When
Projects table 6px red dot left of project IDYou open the project and view the thread
Calendar event 5px red dot on the inspectionYou open the linked project
Notification bellGold badge with total unread countMessages are read
Morning BriefingOpen/unanswered/aging stat countersConversations are addressed
Red Dot Priority On calendar events, RFI dots take precedence over message dots. A project with both an open RFI and unread messages shows only the RFI indicator — the RFI is the more urgent signal.
09 Activity Timeline

The project's Activity section is a chronological feed of all events: status changes, file uploads, cert deliveries, invoice payments, and human messages. Conversations are woven into the timeline alongside system events.

Entry TypeAppearance
System eventCompact one-line: icon + description + timestamp. Grey text.
Human messageExpandable snippet with sender name, conversation title, and preview. Neutral left border.
RFI messageSame as human, but with an orange left border and RFI badge.

Filter buttons at the top: All, Messages, Events, RFIs, Files. A "Load more" button appears when the timeline exceeds 50 entries.

10 Notification Routing

When a message is sent, the notification router decides how to alert other participants:

ChannelWhen
PortalAlways — in-app notification + red dot
EmailIf the recipient is offline. Delayed 2 minutes (cancellable if they come online).
WhatsAppField team members (Darius) — critical or time-sensitive only.
SMSIf enabled in notification preferences — critical notifications only.

DND hours are respected. Consolidation batches multiple notifications within a 2-minute window into a single alert. Preferences are configurable per user via the gear icon in the notification panel.

11 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 context/portal/friday/chat-context
Project timeline/portal/chat/project-timeline/{id}

All conversations and messages are stored permanently in SQLite. The WebSocket connection requires a JWT token re-issued via /portal/chat/ws-token (workaround for httpOnly cookie constraints). Messages are sent via REST for reliability, then broadcast via WebSocket for immediacy.