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>
20 KiB
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:
- Context-switch out of FlowPilot
- Mentally summarize what they've done
- Write the update themselves (different tone for ticket notes vs. client emails)
- 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):
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):
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):
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}/notesConnectWise 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:
{
"audience": "ticket_notes" | "client_update"
}
Response body:
{
"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.pyalready hascreate_ticket_note()- Add a new helper or reuse existing:
push_status_update_to_psa(session, content, is_internal) internalAnalysisFlag=truefor ticket notes,falsefor client updates
Data Model
No new tables needed. Status updates are stored as regular session messages:
ai_session_stepswithstep_type = 'status_update'contentJSONB 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:
<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
hasPsaTicketis 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:
generateStatusUpdate: (sessionId: string, audience: 'ticket_notes' | 'client_update') =>
apiClient.post(`/ai-sessions/${sessionId}/status-update`, { audience })
Hook Extension
Add to useFlowPilotSession:
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:
{
"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
- After update is generated, "Set Reminder" button appears
- Quick options: 15 min, 30 min, 1 hour, Custom
- 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
- 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':
{
"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:
- Engineer clicks Resolve → types summary → auto-generated documentation → done
Enhanced flow:
- Engineer clicks Resolve → types summary → auto-generated documentation
- "Share Resolution" step appears immediately after, with the same options:
- Audience: Ticket Notes / Client Update / Email Draft
- Length: Quick / Detailed
- Pre-generated based on team's default settings (no extra clicks if defaults are right)
- Engineer can Copy, Post to Ticket, or skip
Resolution-Specific Content
Ticket Notes (Resolution)
**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)
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)
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 additionalcontext: "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)
- Backend:
generate_status_update()inflowpilot_engine.py+ endpoint inai_sessions.py - Frontend:
StatusUpdateModalwith 2-step selection (audience + length) + "Share Update" button in action bar - Three audiences: Ticket Notes, Client Update, Email Draft
- Two lengths: Quick and Detailed
- Copy to clipboard functionality
- Client name auto-insertion from intake fields or PSA ticket context
- Store as session step (
step_type = 'status_update') - Resolution communication — "Share Resolution" step after resolve, same audience/length options
- Escalation communication — "Share Escalation" step after escalate, same options
Phase 2: Update History + Reminders
- Update history sidebar — mini-timeline of all updates/resolutions sent during session
- Follow-up reminders — set timer after sending update, in-app notification when it fires
- Auto-dismiss reminders when session resolves before timer
Phase 3: Team Templates
- Communication Templates in Account Settings — tone, sign-off, format preferences
- Template-aware generation — system prompts incorporate team's configured tone/style
Phase 4: PSA Push (implement with ConnectWise integration)
- Post to Ticket button — push to ConnectWise as internal or external note
- PSA status indicator — show success/failure after push
Phase 5: Chat Integration (polish)
- Intent detection — recognize status update requests in natural language
- Inline generation — audience buttons + update rendered in chat stream
- "Since last update" awareness — reference changes since previous status update
- 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)