CI surfaced react-hooks/set-state-in-effect on the synchronous setState(computeState(token)) inside the useEffect body. The earlier shape mirrored token -> state via an effect, which is exactly the "you might not need an effect" pattern React 19's eslint rule now flags. Switch to derived state: compute during render, use a useReducer tick to force re-render on the 30s cadence (so relative timestamps stay current even when token props don't change). Same observable behavior, no cascading renders. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
602 lines
20 KiB
Markdown
602 lines
20 KiB
Markdown
# Mid-Session Status Updates — Feature Spec
|
|
|
|
> **Status:** IMPLEMENT NOW
|
|
> **Date:** 2026-03-23
|
|
> **Priority:** High — addresses real MSP workflow need during active troubleshooting
|
|
|
|
---
|
|
|
|
## Problem
|
|
|
|
Engineers are mid-ticket, actively troubleshooting in FlowPilot, and need to share progress. Today they have to:
|
|
|
|
1. Context-switch out of FlowPilot
|
|
2. Mentally summarize what they've done
|
|
3. Write the update themselves (different tone for ticket notes vs. client emails)
|
|
4. Paste it into their PSA or email
|
|
|
|
This breaks flow, wastes time, and produces inconsistent documentation. The AI already has full context — it should generate the update.
|
|
|
|
## Solution
|
|
|
|
**"Share Update"** — a button in the FlowPilot action bar that generates a context-aware status summary, tailored for either internal ticket notes or client-facing communication.
|
|
|
|
---
|
|
|
|
## User Flow
|
|
|
|
### 1. Trigger
|
|
|
|
Two ways to trigger:
|
|
|
|
- **Action bar button:** "Share Update" button (blue/cyan, positioned between Escalate and Pause)
|
|
- **Chat command:** Engineer types "status update", "give me an update", "share progress" — FlowPilot recognizes the intent
|
|
|
|
### 2. Two-Step Selection
|
|
|
|
**Step 1 — Audience:**
|
|
|
|
FlowPilot responds:
|
|
|
|
> *"Who is this update for?"*
|
|
>
|
|
> **[Ticket Notes]** — Technical, for your PSA
|
|
> **[Client Update]** — Professional, non-technical
|
|
> **[Email Draft]** — Full email with subject line and sign-off
|
|
|
|
These are rendered as clickable option cards in the modal (or inline buttons in chat).
|
|
|
|
**Step 2 — Length:**
|
|
|
|
> *"How detailed?"*
|
|
>
|
|
> **[Quick]** — 1-2 sentences, just the essentials
|
|
> **[Detailed]** — Full breakdown with steps and findings
|
|
|
|
Both steps are single-click. If the engineer triggers via chat with a specific phrase (e.g., "quick update for the client"), both steps are skipped.
|
|
|
|
### 3. AI Generates Summary
|
|
|
|
Based on the full session context (all messages, steps tried, current diagnosis), AI generates the appropriate summary.
|
|
|
|
#### Ticket Notes Mode
|
|
|
|
- **Tone:** Technical, concise, factual (customizable per team — see Team Templates below)
|
|
- **Format:** PSA-compatible markdown (ConnectWise supports markdown in notes)
|
|
- **Content includes:**
|
|
- Current status (investigating / identified / implementing fix)
|
|
- Steps completed and findings
|
|
- What's been ruled out
|
|
- Current hypothesis / next steps
|
|
- Time spent so far
|
|
|
|
**Example output (Detailed):**
|
|
|
|
```
|
|
**Status: Investigating**
|
|
**Time spent: 22 minutes**
|
|
**Steps completed:**
|
|
- Verified MX records — correct (pointing to Exchange Online)
|
|
- Ran message trace in EAC — emails queuing at transport layer
|
|
- Checked recipient mailbox — not full, no forwarding rules
|
|
- Reviewed mail flow rules — found suspicious tenant-wide transport rule
|
|
|
|
**Current diagnosis:**
|
|
Transport rule "Block External Senders" appears to be scoped tenant-wide instead of per-group. This is likely blocking all external inbound mail.
|
|
|
|
**Next steps:**
|
|
- Confirm rule scope with client before modifying
|
|
- Test with a single mailbox first if client approves
|
|
```
|
|
|
|
**Example output (Quick):**
|
|
|
|
```
|
|
Investigating email delivery issue — 22 min in. Traced to a tenant-wide transport rule blocking external senders. Need client approval to modify scope.
|
|
```
|
|
|
|
#### Client Update Mode
|
|
|
|
- **Tone:** Professional, reassuring, non-technical (customizable per team — see Team Templates below)
|
|
- **Format:** Plain text (suitable for portal message or pasting into chat)
|
|
- **Content includes:**
|
|
- What we're working on (plain language)
|
|
- What we've found so far
|
|
- What we're doing next
|
|
- Expected next update time (if inferable)
|
|
- **Content NEVER includes:**
|
|
- Technical jargon (no "transport rules", "MX records", "EAC")
|
|
- Server names, IP addresses, internal tool names
|
|
- Anything that would confuse a non-technical stakeholder
|
|
- **Client name auto-insertion:** If the session has a client/company from intake fields or PSA ticket context, the update addresses them by name ("Hi Acme Medical Group" not generic "Hi")
|
|
|
|
#### Email Draft Mode
|
|
|
|
- **Tone:** Same as Client Update but wrapped in full email structure
|
|
- **Format:** Complete email ready to paste into Outlook/Gmail
|
|
- **Content includes:**
|
|
- Subject line (e.g., "Update: Email delivery issue — [Ticket #]")
|
|
- Greeting with client name (if available)
|
|
- Body (same content as Client Update)
|
|
- Professional sign-off with engineer's name (from user profile)
|
|
- **Use case:** Many MSPs still communicate via email, not PSA portals
|
|
|
|
**Example output (Detailed):**
|
|
|
|
```text
|
|
Hi Acme Medical Group,
|
|
|
|
We're actively working on the email delivery issue. Here's where we stand:
|
|
|
|
We've confirmed that your email system's core settings are correct — the issue isn't with your email addresses or mailboxes. We've traced it to a mail routing configuration that's preventing incoming emails from being delivered.
|
|
|
|
We've identified the specific setting that needs to be adjusted and will confirm the change with you before making it. Once approved, we expect delivery to resume within minutes.
|
|
|
|
We'll update you again shortly.
|
|
|
|
Best regards,
|
|
Michael
|
|
```
|
|
|
|
**Example output (Quick):**
|
|
|
|
```text
|
|
Hi Acme Medical Group — quick update on the email issue. We've identified the cause and have a fix ready. Just need your approval before making the change. Expect resolution within 15 minutes after that. We'll follow up shortly.
|
|
```
|
|
|
|
**Example output (Email Draft):**
|
|
|
|
```text
|
|
Subject: Update: Email delivery issue — TKT-2847
|
|
|
|
Hi Acme Medical Group,
|
|
|
|
Just a quick update on the email delivery issue you reported.
|
|
|
|
We've completed our investigation and identified the cause — a mail routing configuration is preventing incoming emails from being delivered. We have a fix ready and just need your approval before making the change.
|
|
|
|
Once approved, we expect email delivery to resume within minutes.
|
|
|
|
Please let us know if you have any questions.
|
|
|
|
Best regards,
|
|
Michael Chihlas
|
|
ResolutionFlow
|
|
```
|
|
|
|
### 4. Actions After Generation
|
|
|
|
The summary appears in the chat as a formatted message with action buttons:
|
|
|
|
| Button | Action |
|
|
| ------ | ------ |
|
|
| **Copy** | Copy to clipboard (toast: "Copied to clipboard") |
|
|
| **Post to Ticket** | Push directly to ConnectWise ticket as a note (only visible if PSA connected + ticket linked) |
|
|
| **Regenerate** | Generate a new version (same audience + length) |
|
|
| **Switch Audience** | Switch between ticket notes ↔ client update ↔ email draft |
|
|
| **Switch Length** | Toggle quick ↔ detailed |
|
|
| **Set Reminder** | Schedule a follow-up reminder (see Follow-up Reminders below) |
|
|
|
|
**Post to Ticket details:**
|
|
|
|
- Ticket Notes → posted as **Internal Note** (`internalAnalysisFlag: true`)
|
|
- Client Update → posted as **External Note** (`internalAnalysisFlag: false`) — visible to client in their portal
|
|
- Uses existing `POST /service/tickets/{id}/notes` ConnectWise endpoint
|
|
- Uses existing PSA connection and member mapping from the session
|
|
|
|
### 5. Multiple Updates Per Session
|
|
|
|
Engineers can request status updates multiple times during a session. Each update:
|
|
|
|
- Uses the full conversation context up to that point
|
|
- References what changed since the last update (if applicable)
|
|
- Gets saved in the session message history (so it's part of the final documentation)
|
|
|
|
---
|
|
|
|
## Backend Implementation
|
|
|
|
### New Endpoint
|
|
|
|
```
|
|
POST /ai-sessions/{session_id}/status-update
|
|
```
|
|
|
|
**Request body:**
|
|
|
|
```json
|
|
{
|
|
"audience": "ticket_notes" | "client_update"
|
|
}
|
|
```
|
|
|
|
**Response body:**
|
|
|
|
```json
|
|
{
|
|
"content": "string — the generated update",
|
|
"audience": "ticket_notes" | "client_update",
|
|
"session_status": "investigating" | "identified" | "implementing",
|
|
"steps_completed": 5,
|
|
"time_spent_minutes": 22,
|
|
"generated_at": "2026-03-23T14:30:00Z"
|
|
}
|
|
```
|
|
|
|
### FlowPilot Engine Addition
|
|
|
|
Add `generate_status_update()` to `flowpilot_engine.py`:
|
|
|
|
- Loads session + all steps/messages
|
|
- Builds a status-update-specific system prompt based on audience
|
|
- Calls the AI model with full conversation context
|
|
- Returns formatted update
|
|
|
|
**System prompt guidance (ticket notes):**
|
|
|
|
```
|
|
You are generating an internal status update for a PSA ticket note.
|
|
Be technical, concise, and factual. Use markdown formatting.
|
|
Include: current status, steps completed, findings, what's been ruled out, next steps.
|
|
Do NOT soften language or add pleasantries.
|
|
```
|
|
|
|
**System prompt guidance (client update):**
|
|
|
|
```
|
|
You are generating a client-facing status update.
|
|
Be professional, reassuring, and non-technical.
|
|
NEVER use technical jargon, server names, IP addresses, or internal tool names.
|
|
Explain findings in plain language a non-technical business owner would understand.
|
|
Include: what we're working on, what we've found, what's next.
|
|
Keep it brief — 3-5 short paragraphs maximum.
|
|
```
|
|
|
|
### PSA Push (ConnectWise)
|
|
|
|
Reuse existing infrastructure from session resolution:
|
|
|
|
- `services/psa/connectwise/client.py` already has `create_ticket_note()`
|
|
- Add a new helper or reuse existing: `push_status_update_to_psa(session, content, is_internal)`
|
|
- `internalAnalysisFlag` = `true` for ticket notes, `false` for client updates
|
|
|
|
### Data Model
|
|
|
|
No new tables needed. Status updates are stored as regular session messages:
|
|
|
|
- `ai_session_steps` with `step_type = 'status_update'`
|
|
- `content` JSONB includes: `{ audience, generated_content, psa_push_status }`
|
|
|
|
This keeps updates in the session timeline and they'll appear in the final documentation.
|
|
|
|
---
|
|
|
|
## Frontend Implementation
|
|
|
|
### FlowPilotActionBar Changes
|
|
|
|
Add "Share Update" button to the action bar between Escalate and Pause:
|
|
|
|
```tsx
|
|
<button
|
|
onClick={onShareUpdate}
|
|
className="flex items-center gap-2 rounded-lg bg-cyan-500/10 border border-cyan-500/20 px-4 py-2 min-h-[44px] text-sm font-medium text-cyan-400 hover:bg-cyan-500/20 transition-colors"
|
|
>
|
|
<FileText size={16} />
|
|
Share Update
|
|
</button>
|
|
```
|
|
|
|
### StatusUpdateModal Component
|
|
|
|
New modal that handles the audience selection + generated output:
|
|
|
|
**State 1: Audience Selection**
|
|
|
|
- Title: "Share Status Update"
|
|
- Two large option cards: "Ticket Notes" and "Client Update"
|
|
- Each card has icon, title, description
|
|
|
|
**State 2: Generating**
|
|
|
|
- Loading spinner with "Generating update..."
|
|
- Shows audience selected
|
|
|
|
**State 3: Result**
|
|
|
|
- Formatted preview of the generated update
|
|
- Action buttons: Copy, Post to Ticket (conditional), Regenerate, Switch Audience
|
|
- "Post to Ticket" only visible when `hasPsaTicket` is true
|
|
|
|
### Chat Integration
|
|
|
|
When triggered via chat (engineer types "status update"):
|
|
|
|
- FlowPilot detects the intent in the normal message flow
|
|
- Responds with audience selection buttons inline (same as existing action buttons pattern)
|
|
- After selection, generates and displays the update in the chat stream
|
|
- Copy/Post to Ticket buttons appear below the update message
|
|
|
|
### API Client
|
|
|
|
Add to `frontend/src/api/aiSessions.ts`:
|
|
|
|
```typescript
|
|
generateStatusUpdate: (sessionId: string, audience: 'ticket_notes' | 'client_update') =>
|
|
apiClient.post(`/ai-sessions/${sessionId}/status-update`, { audience })
|
|
```
|
|
|
|
### Hook Extension
|
|
|
|
Add to `useFlowPilotSession`:
|
|
|
|
```typescript
|
|
generateStatusUpdate: async (audience: 'ticket_notes' | 'client_update') => {
|
|
const result = await aiSessionsApi.generateStatusUpdate(sessionId, audience)
|
|
return result
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Chat Intent Detection
|
|
|
|
Add status update detection to the FlowPilot engine's intent recognition:
|
|
|
|
**Trigger phrases:**
|
|
|
|
- "status update"
|
|
- "give me an update"
|
|
- "share progress"
|
|
- "write a summary"
|
|
- "update for the ticket"
|
|
- "update for the client"
|
|
- "what should I tell the client"
|
|
- "ticket note"
|
|
|
|
If the phrase specifies audience and/or length (e.g., "quick update for the client"), skip those steps and generate directly.
|
|
|
|
---
|
|
|
|
## Team Communication Templates
|
|
|
|
Team admins can customize the default tone and format for status updates via Account Settings → Communication Templates.
|
|
|
|
### Configurable settings
|
|
|
|
| Setting | Options | Default |
|
|
| ------- | ------- | ------- |
|
|
| Client tone | Professional, Friendly, Formal | Professional |
|
|
| Client sign-off | Custom text or auto (engineer name) | Auto |
|
|
| Company name in sign-off | Include / Exclude | Include |
|
|
| Ticket notes format | Markdown, Plain text | Markdown |
|
|
| Default length | Quick, Detailed | Detailed |
|
|
|
|
### Tone examples
|
|
|
|
**Professional (default):**
|
|
> "We've identified the cause and have a fix ready."
|
|
|
|
**Friendly:**
|
|
> "Good news — we've found what's causing this and we're ready to fix it!"
|
|
|
|
**Formal:**
|
|
> "We wish to inform you that we have completed our investigation and identified the root cause of the reported issue."
|
|
|
|
### Storage
|
|
|
|
Templates stored in `accounts` table as JSONB column `communication_templates`:
|
|
|
|
```json
|
|
{
|
|
"client_tone": "professional",
|
|
"client_signoff": "auto",
|
|
"include_company_in_signoff": true,
|
|
"ticket_notes_format": "markdown",
|
|
"default_length": "detailed"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Follow-up Reminders
|
|
|
|
After generating a status update, the engineer can set a reminder to send another one.
|
|
|
|
### Flow
|
|
|
|
1. After update is generated, "Set Reminder" button appears
|
|
2. Quick options: **15 min**, **30 min**, **1 hour**, **Custom**
|
|
3. When the timer fires:
|
|
- In-app notification: *"Time to send another update to [Client Name] on the email delivery issue"*
|
|
- Click notification → opens the session with the Share Update modal pre-loaded
|
|
5. If the session was resolved before the reminder fires, auto-dismiss with a note: *"Reminder cancelled — session resolved"*
|
|
|
|
### Storage
|
|
|
|
Reminders stored as `ai_session_steps` with `step_type = 'update_reminder'`:
|
|
|
|
```json
|
|
{
|
|
"remind_at": "2026-03-23T15:00:00Z",
|
|
"audience": "client_update",
|
|
"status": "pending" | "fired" | "cancelled"
|
|
}
|
|
```
|
|
|
|
Checked via existing APScheduler interval job or a lightweight frontend timer (for v1, frontend `setTimeout` is sufficient since the session is active).
|
|
|
|
---
|
|
|
|
## Update History Sidebar
|
|
|
|
A mini-timeline in the session sidebar showing all status updates sent during the session.
|
|
|
|
### What it shows
|
|
|
|
Each entry displays:
|
|
|
|
- **Audience icon:** clipboard (ticket), user (client), mail (email draft)
|
|
- **Timestamp:** "2:15 PM"
|
|
- **Length badge:** "Quick" or "Detailed"
|
|
- **Delivery status:** Copied / Posted to ticket / Pending reminder
|
|
- **Click to expand:** Shows the full generated text
|
|
|
|
### Location
|
|
|
|
Inside the existing session sidebar (right side of FlowPilot), below the session info section. Collapsible: "Updates (3)" header that expands to show the timeline.
|
|
|
|
### Why this matters
|
|
|
|
Engineers lose track of what they've already communicated, especially on long sessions. This prevents duplicate updates and lets them reference what they told the client earlier.
|
|
|
|
---
|
|
|
|
## Resolution Communication
|
|
|
|
The same audience/length system applies when an engineer **resolves** a session — this is where it matters most.
|
|
|
|
### Enhanced Resolve Flow
|
|
|
|
**Current flow:**
|
|
|
|
1. Engineer clicks Resolve → types summary → auto-generated documentation → done
|
|
|
|
**Enhanced flow:**
|
|
|
|
1. Engineer clicks Resolve → types summary → auto-generated documentation
|
|
2. **"Share Resolution"** step appears immediately after, with the same options:
|
|
- Audience: Ticket Notes / Client Update / Email Draft
|
|
- Length: Quick / Detailed
|
|
3. Pre-generated based on team's default settings (no extra clicks if defaults are right)
|
|
4. Engineer can Copy, Post to Ticket, or skip
|
|
|
|
### Resolution-Specific Content
|
|
|
|
#### Ticket Notes (Resolution)
|
|
|
|
```text
|
|
**Status: Resolved**
|
|
**Time spent: 35 minutes**
|
|
**Root cause: Tenant-wide transport rule blocking external senders**
|
|
|
|
**Investigation steps:**
|
|
- Verified MX records — correct
|
|
- Ran message trace — emails queuing at transport layer
|
|
- Checked recipient mailbox — no issues
|
|
- Identified transport rule "Block External" scoped tenant-wide instead of per-group
|
|
|
|
**Resolution:**
|
|
Modified transport rule scope from tenant-wide to security group "External-Block-Group". Verified mail flow resumed within 5 minutes. Confirmed with end user that emails are now being received.
|
|
|
|
**Recommendations:**
|
|
- Review all tenant-wide transport rules quarterly
|
|
- Document rule changes in change management system
|
|
```
|
|
|
|
#### Client Update (Resolution)
|
|
|
|
```text
|
|
Hi Acme Medical Group,
|
|
|
|
Great news — the email delivery issue is resolved.
|
|
|
|
We found that a mail routing setting was preventing incoming emails from being delivered to your mailboxes. We've corrected the configuration, and email delivery has been confirmed working.
|
|
|
|
If you or your team notice any further issues with email, please don't hesitate to reach out.
|
|
|
|
Best regards,
|
|
Michael
|
|
```
|
|
|
|
#### Email Draft (Resolution)
|
|
|
|
```text
|
|
Subject: Resolved: Email delivery issue — TKT-2847
|
|
|
|
Hi Acme Medical Group,
|
|
|
|
I'm happy to report that the email delivery issue has been resolved.
|
|
|
|
The cause was a mail routing configuration that was blocking incoming emails. We've corrected this, and your team should now be receiving emails normally. We verified delivery is working before closing the ticket.
|
|
|
|
If you notice any further issues, please let us know and we'll investigate immediately.
|
|
|
|
Best regards,
|
|
Michael Chihlas
|
|
ResolutionFlow
|
|
```
|
|
|
|
### How It Integrates
|
|
|
|
- Reuses the exact same `generate_status_update()` backend function, with an additional `context: "resolution"` parameter
|
|
- The resolution summary the engineer typed feeds into the generation as the authoritative "what fixed it"
|
|
- The existing resolve endpoint (`POST /ai-sessions/{id}/resolve`) returns documentation as before, but now the frontend shows the "Share Resolution" step after
|
|
- If ConnectWise is connected, "Post to Ticket" pushes the resolution note alongside the existing auto-documentation push
|
|
|
|
### Escalation Communication
|
|
|
|
Same system for escalations — when an engineer escalates, offer:
|
|
|
|
- **Ticket Notes:** Technical handoff note (what was tried, what failed, why it's being escalated)
|
|
- **Client Update:** "We're bringing in a specialist to look at this more closely..."
|
|
- **Email Draft:** Full email with escalation context
|
|
|
|
---
|
|
|
|
## Implementation Plan
|
|
|
|
### Phase 1: Core (implement now)
|
|
|
|
1. **Backend:** `generate_status_update()` in `flowpilot_engine.py` + endpoint in `ai_sessions.py`
|
|
2. **Frontend:** `StatusUpdateModal` with 2-step selection (audience + length) + "Share Update" button in action bar
|
|
3. **Three audiences:** Ticket Notes, Client Update, Email Draft
|
|
4. **Two lengths:** Quick and Detailed
|
|
5. **Copy to clipboard** functionality
|
|
6. **Client name auto-insertion** from intake fields or PSA ticket context
|
|
7. **Store as session step** (`step_type = 'status_update'`)
|
|
8. **Resolution communication** — "Share Resolution" step after resolve, same audience/length options
|
|
9. **Escalation communication** — "Share Escalation" step after escalate, same options
|
|
|
|
### Phase 2: Update History + Reminders
|
|
|
|
1. **Update history sidebar** — mini-timeline of all updates/resolutions sent during session
|
|
2. **Follow-up reminders** — set timer after sending update, in-app notification when it fires
|
|
3. **Auto-dismiss reminders** when session resolves before timer
|
|
|
|
### Phase 3: Team Templates
|
|
|
|
1. **Communication Templates** in Account Settings — tone, sign-off, format preferences
|
|
2. **Template-aware generation** — system prompts incorporate team's configured tone/style
|
|
|
|
### Phase 4: PSA Push (implement with ConnectWise integration)
|
|
|
|
1. **Post to Ticket** button — push to ConnectWise as internal or external note
|
|
2. **PSA status indicator** — show success/failure after push
|
|
|
|
### Phase 5: Chat Integration (polish)
|
|
|
|
1. **Intent detection** — recognize status update requests in natural language
|
|
2. **Inline generation** — audience buttons + update rendered in chat stream
|
|
3. **"Since last update" awareness** — reference changes since previous status update
|
|
4. **Shorthand commands** — "quick update for the client" skips both selection steps
|
|
|
|
---
|
|
|
|
## Edge Cases
|
|
|
|
- **No messages yet:** Disable "Share Update" until at least 2 message exchanges
|
|
- **Session paused/resolved:** Still allow generating updates (useful for post-session documentation)
|
|
- **No PSA connected:** Hide "Post to Ticket" button, only show Copy
|
|
- **PSA push fails:** Show error toast, keep the generated content available for manual copy
|
|
- **Multiple rapid requests:** Debounce — disable button for 3 seconds after generation
|
|
- **Very short session:** AI should still produce a useful update, even if minimal ("Currently investigating — gathering initial information")
|
|
|
|
---
|
|
|
|
## Success Metrics
|
|
|
|
- % of sessions where "Share Update" is used (target: 30%+ of sessions >10 minutes)
|
|
- Time between update generation and ticket note creation (should be <5 seconds with Post to Ticket)
|
|
- Net Promoter feedback on update quality (post-pilot survey)
|