docs: add FlowPilot migration design doc and mockups

Brings the locked FlowPilot migration design onto the branch that will
implement it. Includes the annotated target UI mockups (primary session
view + three Script Generator integration states) and the superseded
FLOWPILOT-AND-RESOLUTIONASSIST.md for historical reference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-17 15:22:39 +00:00
parent f0ccf313a4
commit 46291f30b9
8 changed files with 3793 additions and 0 deletions

View File

@@ -0,0 +1,163 @@
# FlowPilot & ResolutionAssist
> ResolutionFlow offers two AI-driven troubleshooting modes that share the same session backend but present very different interaction styles. Both work standalone and become richer when paired with a PSA connection.
---
## At a glance
| | **FlowPilot** | **ResolutionAssist** |
|---|---|---|
| **Style** | Guided, structured | Conversational, freeform |
| **Entry** | `/pilot` | `/assistant` |
| **Interaction** | Questions → Actions → Resolution, one step at a time | Natural chat with inline questions/actions |
| **Best for** | Reproducible workflows, low-context engineers, handoffs | Exploratory problems, quick lookups, rubber-ducking |
| **Lifecycle** | Active → Paused → Resolved / Escalated / Abandoned | Active → Resolved / Abandoned (lightweight) |
| **Confidence tracking** | Yes — drives tier transitions | No — always responsive to user direction |
| **Navigation guard** | Yes — prevents accidental loss | No — free to leave and return |
Both modes share the `ai_sessions` table (discriminated by `session_type`), the same multimodal AI backend (image uploads, markdown, cached prompts), and the same `[QUESTIONS]` / `[ACTIONS]` / `[FORK]` marker vocabulary that renders inline TaskLane elements.
---
## FlowPilot — guided troubleshooting
FlowPilot is a wizard-style AI engineer that walks you through a problem one diagnostic step at a time. It runs on confidence tiers:
- **Discovery** (confidence < 0.4) — asking broad, open-ended questions to characterize the problem
- **Exploring** (0.40.8) — proposing targeted actions and narrowing hypotheses
- **Guided** (≥ 0.8) — recommending a specific fix with steps to verify
### The FlowPilot session flow
1. **Intake.** You start from `/pilot` or from the dashboard "New Session" button. The intake screen accepts free-text description, PSA ticket context, screenshots, or log pastes.
2. **Preference check.** Before suggesting any fix, the AI asks whether you want a **GUI** or **script** approach. This is enforced in the system prompt so you never get steps you can't execute.
3. **Step-by-step progression.** Each AI response is either a question (with clickable options), an action (with "Done" / "Didn't work" buttons), a `[FORK]` (two distinct paths to try), or a final resolution suggestion. You respond, the AI updates its confidence, and the next step is generated.
4. **Action bar.** The session header always shows **Pause & Leave**, **Resolve**, **Escalate**, **Share Update**, and **Close**. Pausing freezes the session; resuming restores the full context.
5. **Resolve / Escalate.** *Resolve* marks the ticket fixed and generates a clean summary of what worked. *Escalate* packages the problem summary and steps tried into an **escalation package** that the next engineer (or the PSA ticket) inherits.
### Why FlowPilot exists
- **New engineers** get senior-engineer-level diagnostic rigor without needing the experience to know what to ask next.
- **Documented resolutions** — every step is captured, so the generated note on the ticket is substantive (not just "fixed it").
- **Handoff-friendly** — escalation packages mean the next person doesn't start from zero.
---
## ResolutionAssist — conversational AI
ResolutionAssist is a chat with an expert IT systems engineer. It's less structured than FlowPilot but still surfaces interactive elements when the AI wants structured input.
### The ResolutionAssist flow
1. **Open a chat.** From `/assistant` or the dashboard. Sessions show up in the left sidebar just like any messaging app.
2. **Send a message.** Freeform prose. Attach up to 3 images per message (screenshots, error dialogs, network diagrams). Paste logs, code, or PowerShell output.
3. **AI responds.** The response is prose, but any `[QUESTIONS]` or `[ACTIONS]` blocks render as a **TaskLane** — a side panel with clickable options and action buttons. You can answer via chat or click the TaskLane elements.
4. **Branching (`[FORK]`).** If the AI proposes two paths ("check cable or restart switch?"), the fork renders as a choice. Picking one continues the conversation down that path.
5. **Resume later.** Unlike FlowPilot, there's no navigation guard. Leave mid-conversation; every message is stored.
### Why ResolutionAssist exists
- **Unstructured problems** — "I have no idea where to start, here's a screenshot" works great.
- **Reference lookups** — "what's the right PowerShell command to check Exchange health" is faster in chat than through an intake form.
- **Senior engineers** — when you already know what you're doing and just want a second opinion or a syntax check.
---
## Without a PSA connection
Both modes work standalone. Without ConnectWise connected:
- Sessions live entirely in ResolutionFlow. They're listed in your session history, searchable, and shareable via public share links (`/shared/sessions/:token`).
- Summaries generated on Resolve are saved to the session record but **not** written anywhere else. You can copy/paste into whatever ticketing or documentation system you use.
- Escalating a FlowPilot session routes the escalation package to another ResolutionFlow engineer on your team — not to an external PSA ticket.
- No ticket context is injected into the AI prompt, so the AI starts cold with only what you provide in the intake or first message.
**Standalone use cases:**
- Evaluating ResolutionFlow before committing to a PSA integration
- Troubleshooting internal IT issues that aren't client-facing
- Teams using a PSA ResolutionFlow doesn't integrate with yet
- Knowledge-base research ("what are my options for X") that don't map to a ticket
---
## With a PSA connection (ConnectWise)
When ConnectWise is connected, both modes become ticket-aware and write back to the PSA as a first-class client.
### FlowPilot + PSA
**Starting from a ticket:**
- Click a ticket row (from `/tickets` or the dashboard queue) and pick "Start FlowPilot." The ticket's problem description, recent notes, configurations, company details, and related tickets are auto-injected into the AI's context. No manual retyping.
- The session shows the linked ticket badge in the header.
**During the session:**
- **Share Update** — posts an interim note to the CW ticket with the current AI summary, so stakeholders can see progress without interrupting you.
- **Status changes** — the detail panel and session header let you move the ticket through statuses (New → In Progress → Waiting on Customer → Resolved) directly from ResolutionFlow. Status writes are verified against CW so you're never told "success" when CW silently rejected the change.
- **Resource assignment** — add yourself or a teammate as a co-assignee without touching the owner. If the ticket has no owner yet, assigning sets owner; if there's already an owner, you're added as an additional resource via a CW schedule entry.
**On Resolve:**
- Final summary is posted as a ticket note.
- Ticket status can auto-update to Resolved (per your team's settings).
**On Escalate:**
- The escalation package (problem summary + steps tried) is posted as a note.
- The ticket can be routed via CW's normal escalation rules.
- The next engineer picking up the ticket can auto-start a new session with the full escalation context pre-filled.
**Spin-off tickets (new):**
- During any session, if you discover a separate issue, the AI can propose `create_spin_off_ticket`. Accepting opens the New Ticket modal pre-filled with the current ticket's company and board, so a second ticket is one click away without leaving your session.
### ResolutionAssist + PSA
**Starting from a ticket:**
- Same ticket-context injection as FlowPilot. When opened with a linked ticket, the AI sees company, configs, notes, and related tickets.
- A "New Ticket" button appears in the header — lets you spawn a separate ticket mid-conversation (same flow as FlowPilot's spin-off).
**During the chat:**
- Ask the AI about the ticket directly: *"Summarize what's been tried," "What configs does this company have?"* — the AI already has that context loaded.
- `[ACTIONS]` can include `create_spin_off_ticket` when the AI detects a separate issue surfaced in the conversation.
**Writing back:**
- ResolutionAssist is a lighter-weight mode, so it doesn't auto-post on resolve. You can manually copy the conversation summary to a ticket note if useful.
- Status updates and resource assignment are done via the `/tickets` page rather than the chat UI.
---
## Choosing between them
| I want to… | Use |
|---|---|
| Walk through a known issue type with step-by-step rigor | **FlowPilot** |
| Document every action taken for audit or handoff | **FlowPilot** |
| Escalate with a full context package | **FlowPilot** |
| Ask a question, get an answer, move on | **ResolutionAssist** |
| Paste a screenshot and say "what's wrong here?" | **ResolutionAssist** |
| Stay on the ticket for 2 minutes, not 20 | **ResolutionAssist** |
| Troubleshoot without breaking flow to switch pages | Either, with the linked ticket panel open alongside |
The two modes aren't competitive. A common workflow is to start in ResolutionAssist to scope the problem, then kick off a FlowPilot session when you realize the issue is going to take real diagnosis. Both show up in the unified session history.
---
## Tickets page — the PSA hub
`/tickets` is the CW ticket manager built into ResolutionFlow. With a PSA connection:
- Search and filter tickets by assignment (me / unassigned / specific member via searchable picker), board, status, priority, company, open/closed.
- Slide-out detail panel shows notes, configurations, related tickets, and assignees — all fetched in parallel for fast hydration.
- From the detail panel: change status, add/remove assignees, post notes, or "Start FlowPilot" / "Open in ResolutionAssist" with full context.
- New Ticket modal offers both AI-parse ("Create a high-priority ticket for Acme — Outlook not syncing for jsmith") and a traditional form.
Without a PSA connection, `/tickets` is hidden from the sidebar entirely — there's nothing to show.
---
## Summary
- **FlowPilot** = guided, structured, lifecycle-heavy, ideal for resolvable issues and handoffs.
- **ResolutionAssist** = freeform chat, ideal for scoping and quick answers.
- **Without PSA** = both work, sessions live in ResolutionFlow, summaries are yours to export.
- **With PSA** = both become ticket-aware, write back to CW (notes, status, resources), and can spawn spin-off tickets mid-session.
The AI is the same under the hood. The difference is how much structure you want around the conversation — and how deeply the result needs to integrate with your ticketing system.

View File

@@ -0,0 +1,794 @@
# FlowPilot Migration — Design & Implementation Doc
> **Target:** Transform `/assistant` (ResolutionAssist) into the new unified `/pilot` (FlowPilot) surface.
> **Audience:** Claude Code (implementation) reviewed by Michael (owner).
> **Status:** Design locked. Ready for phased implementation.
> **Last updated:** April 17, 2026
---
## 0. Prerequisite reading for Claude Code
Before writing any code, read these in order:
1. This document end-to-end.
2. `mockups/01-session-primary.png` — the target state for the main session UI.
3. `mockups/02-script-template-match.png`, `03-script-three-options.png`, `04-script-templatize-prompt.png` — Script Generator integration states.
4. The source HTML files `mockups/01-session-primary.html` and `mockups/02-04-script-integration.html` — authoritative for spacing, colors, and component structure. When CSS or layout questions arise during implementation, these files are the tiebreaker.
Do not proceed to implementation until you have confirmed you understand the following three architectural claims. If any of them are unclear, stop and ask.
1. **There is one AI troubleshooting surface, not two.** The existing split between FlowPilot (guided) and ResolutionAssist (chat) is collapsed into a single chat-primary product called FlowPilot at `/pilot`. The `ai_sessions.session_type` discriminator column is retained for data, but the product shows one unified UI.
2. **The task lane is the load-bearing structural feature.** It is not a sidebar of metadata. It actively tracks diagnostic state: *What we know*, *Questions*, *Diagnostic checks*, *Suggested fix*. Engineers interact with it; facts flow between sections.
3. **Resolve and Escalate are deterministic artifact generators, not free-text prompts.** When an engineer clicks Resolve, a structured summary is generated from task lane state (not from the chat transcript alone) and posted to CW. The summary structure is fixed: *Problem / What we confirmed / Root cause / Resolution*.
---
## 1. Why this change
### The current state
- `/assistant` is a chat-primary AI session with a `[QUESTIONS]` and `[DIAGNOSTIC_CHECKS]` task lane.
- `/pilot` was specced as a separate guided, confidence-tiered wizard with a different UI and lifecycle.
- The `FLOWPILOT-AND-RESOLUTIONASSIST.md` design document treated them as two products sharing a backend.
### The problems with the current state
- Two sidebar entries, two session histories, two mental models for engineers to learn.
- The PSA integration scope doubles (writebacks for lifecycle events must be built twice, or built for Pilot and bolted onto Assist).
- The Team Wiki moat depends on structured session artifacts with explicit resolutions — a chat-only mode produces weaker artifacts.
- The cockpit positioning (the core ResolutionFlow brand promise) does not map to a blank chat window.
- Branching into two modes forces a decision onto the engineer ("which mode for this ticket?") that has no right answer.
### The resolution
The existing `/assistant` UI already does most of what `/pilot` was supposed to do — structured questions, diagnostic checks, lifecycle actions in the header. It is closer to the right product than the doc anticipated. Rather than building Pilot as a second surface, we extend Assist with the missing structural features (*What we know*, auto-generated summaries, escalation packages) and rename it FlowPilot.
### The strategic move
FlowPilot becomes the single canonical troubleshooting surface. Every PSA writeback, every Wiki compilation path, every Script Generator invocation points here. One session shape, one lifecycle, one integration surface.
---
## 2. Terminology used in this document
| Term | Meaning |
|---|---|
| **Session** | A single `ai_sessions` row representing one troubleshooting conversation. |
| **Task lane** | The right-side panel containing What we know, Questions, Diagnostic checks, Suggested fix. |
| **Fact** | An item in the What we know section. Has `text`, `source_type` (`question` / `diagnostic_check` / `user_note`), and `source_ref` (FK to the originating question/check, or null for user notes). |
| **Suggested fix** | The AI's current best-guess resolution path. Has a confidence score and, optionally, a reference to a Script Library template. |
| **Promotion** | The act of a question answer or diagnostic check result being converted into a fact in What we know. Triggered by AI, confirmed/editable by engineer. |
| **Resolution note** | The structured document generated when the engineer clicks Resolve. Posted to CW as a ticket note. |
| **Escalation package** | The structured handoff document generated when the engineer clicks Escalate. Posted to CW and attached to the session for the next engineer. |
---
## 3. Target UI — annotated
### 3.1 Primary session view
![Primary session view](mockups/01-session-primary.png)
The session UI is a four-column layout:
1. **Icon rail** (64px wide) — primary app navigation. FlowPilot / Tickets / Trees / Scripts / Wiki. Avatar at bottom.
2. **Session list** (260px wide) — all sessions grouped by state (Active / Recent). Each row shows title, state dot, PSA ticket number, and client name.
3. **Conversation column** (fluid) — the chat thread, composer, and incident header.
4. **Task lane** (380px wide) — *What we know*, *Questions*, *Diagnostic checks*, *Suggested fix*, and the Resolve action at the bottom.
Key visual and behavioral elements numbered against the mockup:
**Incident header (top of conversation column)**
- PSA chip showing `CW #48291` in cyan, monospaced
- Client / contact / priority meta line
- Incident title in Bricolage Grotesque 19px
- Four lifecycle buttons right-aligned: **Pause** (ghost), **Share update** (neutral), **Escalate** (amber), **Resolve** (green)
**Conversation column**
- Standard chat thread with pilot and user avatars
- Pilot uses cyan gradient avatar; user uses purple gradient
- AI messages in `bg-2` bubbles with subtle border; user messages in cyan-tinted bubbles
- Composer at bottom with inline action chips (Attach / Paste logs / Ticket context) and a send button
**Task lane sections, in order:**
1. **What we know** (NEW)
- Header: `WHAT WE KNOW · 4` (section title + count)
- Each fact is a card: `bg-2` background, dashed circular green check, fact text, and a provenance line (`from question · rules out tenant/license`)
- "+ Add a note" button at the bottom for manual facts from the engineer
- Background has a subtle green-to-transparent gradient to visually distinguish from the rest of the lane
2. **Questions**
- Header: `QUESTIONS · 2 unanswered`
- Each unanswered question: title, AI hint text, Answer / Skip buttons
- Answered questions dim to 55% opacity with a dashed border and show the resolution inline (`Answered · isolated to jsmith (promoted to What we know)`)
3. **Diagnostic checks**
- Header: `DIAGNOSTIC CHECKS · 1 / 3 run`
- "Run remaining 2 checks" button at top when applicable
- Each check: icon + command name (monospaced), description
- Completed checks dim and show "Complete · findings promoted to What we know" in green
4. **Suggested fix**
- Header: `SUGGESTED FIX · 94% confidence`
- Amber-accented card with fix title and description
- Clicking opens the Script Generator flow (Section 5)
**Resolve action bar (bottom of task lane)**
- Small hint text ("Summary preview is open →")
- Full-width "Resolve & post to CW" button in green
**Resolution note preview (floating, anchored to Resolve button)**
- A persistent popover, NOT a modal
- Shows the draft resolution note with Problem / What we confirmed / Root cause / Resolution sections
- Displays the target ticket (`CW #48291`) and status change (`Resolved`)
- Edit button opens an inline editor; Confirm & post fires the PSA writeback
### 3.2 Script Generator integration — template match
![Template match flow](mockups/02-script-template-match.png)
When the suggested fix references an existing Script Library template, clicking the fix opens the Script Generator panel in place of (or sliding over) the task lane. Key behavior:
- A **Verified template** badge appears above the parameter form
- Parameters pre-filled from session context get a cyan `from session` tag and a cyan-tinted input background
- Each pre-filled parameter has a hint line explaining the source: *"Pulled from CW company config for Acme Corp"*
- The engineer can adjust any pre-filled value before generating
- `⌘K` → "script" invokes the generator mid-conversation from anywhere in the session
### 3.3 Script Generator integration — no template match (three-option dialog)
![No template match](mockups/03-script-three-options.png)
When no template matches the suggested fix, FlowPilot drafts a session-specific script and presents three paths:
1. **Run as one-off** (neutral outline CTA)
- Script generated and captured in session documentation, discarded after
- Tradeoffs: fastest, but team won't benefit next time
2. **Run now, templatize after resolve** (RECOMMENDED, cyan primary CTA)
- Script generated for this ticket; draft template queued
- Post-resolve prompt offers to templatize (Section 5.3)
- Tradeoffs: zero cognitive overhead now, only templatize what works, ~30s review later
3. **Build as template now** (purple outline CTA)
- Full parameterization upfront
- Tradeoffs: immediate team benefit, but adds time mid-ticket
The drafted script renders as a code preview above the option cards with the AI's proposed parameters highlighted in amber.
### 3.4 Script Generator integration — post-resolve templatization prompt
![Templatize prompt](mockups/04-script-templatize-prompt.png)
If the engineer picked Option 2 in the three-option dialog and Resolve succeeds, this prompt appears after the resolution note is posted to CW:
- Success banner confirms the resolution posted
- Templatize card shows the script with AI-proposed parameters substituted in as `{{ gateway_host }}`, etc.
- Right pane lists extracted parameters with remove buttons (engineer can adjust)
- Provenance note: *"generated from CW #48307 · resolved by M. Davis"*
- Three actions: Skip / Edit parameters / Save as team template
- "Don't ask me again for this team" opt-out in footer
---
## 4. Data model changes
### 4.1 New columns on `ai_sessions`
```sql
ALTER TABLE ai_sessions
ADD COLUMN resolution_note_markdown TEXT NULL,
ADD COLUMN resolution_note_posted_at TIMESTAMPTZ NULL,
ADD COLUMN resolution_note_external_id VARCHAR(128) NULL, -- CW note ID after posting
ADD COLUMN escalation_package_markdown TEXT NULL,
ADD COLUMN escalation_package_posted_at TIMESTAMPTZ NULL;
```
No migration of `session_type` — the column stays. New sessions all default to the unified FlowPilot type.
### 4.2 New `session_facts` table (the What we know backing store)
```sql
CREATE TABLE session_facts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
session_id UUID NOT NULL REFERENCES ai_sessions(id) ON DELETE CASCADE,
account_id UUID NOT NULL REFERENCES accounts(id), -- for RLS, per multi-tenant architecture
text TEXT NOT NULL,
source_type VARCHAR(32) NOT NULL CHECK (source_type IN ('question', 'diagnostic_check', 'user_note', 'ai_synthesis')),
source_ref UUID NULL, -- FK to session_questions.id or session_diagnostic_checks.id, null for user_note
source_summary TEXT NULL, -- free-text provenance label, e.g. "rules out tenant/license"
created_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
deleted_at TIMESTAMPTZ NULL
);
CREATE INDEX idx_session_facts_session ON session_facts(session_id) WHERE deleted_at IS NULL;
CREATE INDEX idx_session_facts_account ON session_facts(account_id);
```
**Important:** `source_ref` is a polymorphic FK and should NOT have a database-level FK constraint. Enforce integrity at the service layer.
### 4.3 New `session_suggested_fixes` table
```sql
CREATE TABLE session_suggested_fixes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
session_id UUID NOT NULL REFERENCES ai_sessions(id) ON DELETE CASCADE,
account_id UUID NOT NULL REFERENCES accounts(id),
title VARCHAR(200) NOT NULL,
description TEXT NOT NULL,
confidence_pct INTEGER NOT NULL CHECK (confidence_pct BETWEEN 0 AND 100),
script_template_id UUID NULL REFERENCES script_templates(id), -- null if no template match
ai_drafted_script TEXT NULL, -- populated if no template match
ai_drafted_parameters JSONB NULL, -- AI's proposed parameterization
user_decision VARCHAR(32) NULL CHECK (user_decision IN ('one_off', 'draft_template', 'build_template', 'dismissed')),
superseded_at TIMESTAMPTZ NULL, -- set when a new suggestion replaces this one
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_session_suggested_fixes_session ON session_suggested_fixes(session_id) WHERE superseded_at IS NULL;
```
A session can have multiple suggested fixes over time as the AI's understanding evolves. Only one is active (superseded_at IS NULL) at a time.
### 4.4 New `draft_templates` table
Backing store for Option 2 in the three-option dialog — scripts generated during sessions that are pending templatization.
```sql
CREATE TABLE draft_templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
account_id UUID NOT NULL REFERENCES accounts(id),
source_session_id UUID NOT NULL REFERENCES ai_sessions(id),
source_user_id UUID NOT NULL REFERENCES users(id),
script_body TEXT NOT NULL,
proposed_parameters JSONB NOT NULL, -- {"parameters": [{"key": "...", "label": "...", "type": "..."}]}
proposed_name VARCHAR(200) NULL,
proposed_category_id UUID NULL REFERENCES script_categories(id),
status VARCHAR(32) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'accepted', 'rejected')),
resolved_at TIMESTAMPTZ NULL, -- when the user acted on the draft
promoted_template_id UUID NULL REFERENCES script_templates(id), -- if accepted, the created template
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
```
Accepted draft templates produce a new `script_templates` row and record the source session for provenance display.
### 4.5 Extension to `script_templates`
```sql
ALTER TABLE script_templates
ADD COLUMN source_session_id UUID NULL REFERENCES ai_sessions(id),
ADD COLUMN source_user_id UUID NULL REFERENCES users(id),
ADD COLUMN source_ticket_ref VARCHAR(64) NULL; -- e.g. "CW #48307" for display
```
These fields power the provenance chip in the Script Library: *"generated from CW #48307 · resolved by M. Davis · used 7 times"*.
### 4.6 Per-account settings
```sql
ALTER TABLE account_settings
ADD COLUMN templatize_prompt_enabled BOOLEAN NOT NULL DEFAULT true;
```
Controls whether the post-resolve templatize prompt appears. Toggleable from the prompt's footer ("Don't ask me again for this team") and from admin settings.
---
## 5. API endpoints
All endpoints follow ResolutionFlow conventions: `/api/v1/` prefix, JWT auth, tenant-scoped via RLS.
### 5.1 Session facts
```
GET /api/v1/sessions/{id}/facts List facts for a session (ordered by created_at ASC)
POST /api/v1/sessions/{id}/facts Create a manual fact (user_note source_type)
PATCH /api/v1/sessions/{id}/facts/{fact_id} Edit fact text or summary (only for user_note or AI-synthesized facts)
DELETE /api/v1/sessions/{id}/facts/{fact_id} Soft-delete
POST /api/v1/sessions/{id}/facts/promote Promote a question answer or check result to a fact
Body: { source_type, source_ref, proposed_text, proposed_summary }
Returns the created fact. Used by the AI synthesis flow and
by the engineer's explicit "promote to What we know" action.
```
### 5.2 Suggested fixes
```
GET /api/v1/sessions/{id}/suggested-fixes/active Returns the current active fix (superseded_at IS NULL) or 404
POST /api/v1/sessions/{id}/suggested-fixes/{fix_id}/decision
Body: { decision: "one_off" | "draft_template" | "build_template" | "dismissed" }
Records the user's path choice. Server-side side effects:
- one_off: generates script via ScriptTemplateEngine, returns rendered script
- draft_template: same as one_off, plus creates draft_templates row
- build_template: redirects to full template creation flow
- dismissed: marks fix as superseded
```
### 5.3 Draft templates (post-resolve flow)
```
GET /api/v1/draft-templates List pending drafts for the current user's account
(used by the Script Library "X scripts ready to review" notification)
GET /api/v1/draft-templates/{id} Get a single draft including its proposed parameterization
POST /api/v1/draft-templates/{id}/accept Body: { name, category_id, parameters_schema, edits }
Creates a new script_templates row with source_session_id set,
sets draft status to 'accepted', returns the new template
POST /api/v1/draft-templates/{id}/reject Sets status to 'rejected'
```
### 5.4 Resolution notes and escalation packages
```
POST /api/v1/sessions/{id}/resolution-note/preview Generates the draft resolution note from current session state
WITHOUT posting. Returns { markdown, target_ticket_ref }.
Called when the task lane renders and refreshed whenever
facts/suggested fix change.
POST /api/v1/sessions/{id}/resolution-note/post Body: { markdown } (engineer-edited version)
Posts to the linked PSA ticket, updates ticket status if configured,
marks session resolved.
POST /api/v1/sessions/{id}/escalation-package/preview Same pattern for escalation
POST /api/v1/sessions/{id}/escalation-package/post Posts and transitions session to escalated state
```
---
## 6. Services to implement
### 6.1 `FactSynthesisService` (new)
**Location:** `services/fact_synthesis_service.py`
**Purpose:** Converts question answers and diagnostic check results into candidate facts. Called by the AI pipeline when the LLM emits a `[PROMOTE]` marker, and by explicit engineer action.
**Key methods:**
- `synthesize_from_question(question_id: UUID, raw_answer: str) -> dict` — returns `{proposed_text, proposed_summary}` via LLM call. The summary is the short provenance label ("rules out tenant/license").
- `synthesize_from_check(check_id: UUID, check_output: str) -> dict` — same pattern for diagnostic check output.
- `create_fact(session_id, source_type, source_ref, text, summary, user_id) -> SessionFact` — persists the fact.
**Prompt engineering note:** The synthesis prompt should be conservative — short, factual statements. Hallucinated specifics are a trust-killer. The prompt must explicitly instruct: *"Use only information present in the answer/output. If the answer does not contain a substantive fact, return null."*
### 6.2 `ResolutionNoteGeneratorService` (new)
**Location:** `services/resolution_note_generator.py`
**Purpose:** Produces the structured resolution note markdown from session state.
**Input:** session_id
**Output:** `{markdown: str, target_ticket_ref: str | None}`
**Template structure:**
```markdown
## Problem
{ai-synthesized one-paragraph problem statement, pulling from session description + incident header}
## What we confirmed
{bulleted list of session_facts, grouped by source_type}
## Root cause
{ai-synthesized from the active suggested fix + facts}
## Resolution
{description of the fix applied, parameters used if a script ran, outcome}
```
The service pulls from four data sources: `ai_sessions`, `session_facts`, `session_suggested_fixes` (active), and `script_generations` (if scripts ran during the session). Passwords in script_generations.parameters_used must be redacted (already a Script Generator pattern per the existing plan).
**Critical:** This service is called on every fact/suggestion change to keep the preview live. Cache aggressively — LLM calls for every keystroke will blow the budget. Invalidate the cache on any write to session_facts or session_suggested_fixes.
### 6.3 `EscalationPackageGeneratorService` (new)
**Location:** `services/escalation_package_generator.py`
Same structure as ResolutionNoteGenerator but with a handoff-oriented template:
```markdown
## Problem
...
## What we've confirmed
...
## What we've tried
{list of diagnostic_checks run with their outcomes, scripts generated}
## Current hypothesis
{active suggested fix description}
## Suggested next steps
{ai-synthesized from the gap between facts and a complete resolution}
```
### 6.4 `TemplateExtractionService` (new)
**Location:** `services/template_extraction_service.py`
**Purpose:** Given a concrete rendered script and session context, propose a parameterization.
**Input:** `{script_body: str, session_context: dict, ticket_context: dict}`
**Output:** `{parameters: [{key, label, type, inferred_from}], templated_body: str}`
**Implementation approach:**
- LLM call with a structured prompt: "Given this script that resolved a ticket, identify values that would change for a different invocation. Propose a parameter schema following the Script Generator conventions (text / password / select / boolean / multi_text / number / textarea)."
- Post-process to ensure the proposed template renders back to the original script when given the extracted parameter values.
- Conservative default: prefer fewer parameters. If a value looks environment-agnostic (e.g. a command name), don't parameterize it.
This service is the engine behind Option 2 and Option 3 of the three-option dialog, and behind the post-resolve templatize prompt.
### 6.5 Extend `PSAWritebackService` (existing)
Add methods:
- `post_resolution_note(session_id, markdown) -> {external_id, posted_at}`
- `post_escalation_package(session_id, markdown) -> {external_id, posted_at}`
- `transition_ticket_status(ticket_ref, new_status) -> {success, verified_status}`
The `transition_ticket_status` method must verify the status change took effect (per the existing ConnectWise integration principle: "never told 'success' when CW silently rejected the change").
### 6.6 Model selection per service
Each AI-calling service must use a configurable model string from application settings, not a hardcoded model. Use these defaults:
```python
FACT_SYNTHESIS_MODEL = "claude-haiku-4-5-20251001" # short transformation, latency-sensitive
RESOLUTION_NOTE_MODEL = "claude-sonnet-4-6" # customer-facing artifact, quality matters
ESCALATION_PACKAGE_MODEL = "claude-sonnet-4-6" # same
TEMPLATE_EXTRACTION_MODEL = "claude-sonnet-4-6" # creates persistent library artifact
MAIN_CONVERSATION_MODEL = "claude-sonnet-4-6" # primary FlowPilot chat
```
Do not hardcode model strings at call sites. Every new service must read from settings with a service-specific key.
**Instrumentation requirement:** log a `disputed_fact_rate` metric for fact synthesis — the percentage of AI-synthesized facts that engineers subsequently edit or delete. If this exceeds 10% over a 500-session window, escalate `FACT_SYNTHESIS_MODEL` to `claude-sonnet-4-6`. If under 5%, Haiku is performing correctly.
Do not use Opus 4.7 for any of these services at current scale.
---
## 7. Frontend components
### 7.1 Routes to change
| Current route | New route | Action |
|---|---|---|
| `/assistant` | `/pilot` | **Rename** the route. The existing page moves. `/assistant` permanently redirects to `/pilot` with no sunset date. |
| `/pilot` (if it exists as a separate guided flow) | REMOVED | Collapse into the unified surface. |
| `/pilot/session/:id` | `/pilot/session/:id` | No change (this is where the unified session UI lives) |
Sidebar nav entry renames from "ResolutionAssist" to "FlowPilot" with the cockpit icon.
### 7.2 New React components
Under `src/components/pilot/`:
```
TaskLane.tsx -- The right-side panel, owns all four sections
sections/
WhatWeKnow.tsx -- New component for the facts list
WhatWeKnowItem.tsx -- Single fact card with provenance line
AddNoteButton.tsx -- "+ Add a note" inline composer
Questions.tsx -- Existing questions rendering (moved if already present)
DiagnosticChecks.tsx -- Existing checks rendering (moved if already present)
SuggestedFix.tsx -- New or refactored component for the suggested fix card
ResolveButton.tsx -- The Resolve CTA at the bottom of the task lane
ResolutionNotePreview.tsx -- Floating popover anchored to Resolve button
EscalatePackagePreview.tsx -- Same pattern for Escalate
ScriptGenInline/ -- Script Generator embedded in session context
TemplateMatchPanel.tsx -- Scene 1 mockup: template pre-filled
NoTemplateDialog.tsx -- Scene 2 mockup: three-option dialog
TemplatizePrompt.tsx -- Scene 3 mockup: post-resolve prompt
ParameterizationPreview.tsx -- Shared component: script with highlighted params
```
### 7.3 Component behavior contracts
**`WhatWeKnowItem`**
- Props: `{fact: SessionFact, onEdit, onDelete}`
- Renders the fact text, a green checkmark, and the provenance line with source-type color coding
- Clicking the fact text opens inline edit (only for `user_note` and `ai_synthesis` sources — question/check facts are read-only, edit the source instead)
**`TaskLane`**
- Subscribes to a session state hook that polls for fact / question / check / suggested-fix updates
- On any state change, calls `POST /api/v1/sessions/{id}/resolution-note/preview` to refresh the ResolutionNotePreview
- Debounce preview refresh to 500ms to avoid LLM spam
**`NoTemplateDialog`** (three-option dialog)
- Props: `{suggestedFix, onDecision}`
- Renders the three cards with the middle (draft_template) marked as recommended
- `onDecision` posts to `/api/v1/sessions/{id}/suggested-fixes/{fix_id}/decision` and either opens the Script Generator (one_off / draft_template) or navigates to full template creation (build_template)
**`TemplatizePrompt`**
- Rendered after successful Resolve when a draft template exists for the session
- Fetches proposed parameters from the draft template record
- Save button posts to `/api/v1/draft-templates/{id}/accept`
---
## 8. AI prompt changes
The existing FlowPilot / ResolutionAssist system prompt needs updates to emit the new markers.
### 8.1 New marker: `[PROMOTE]`
Used to surface facts to What we know. Syntax:
```
[PROMOTE]
source_type: question
source_ref: {question_id}
text: OWA login and send/receive confirmed working for jsmith
summary: rules out tenant/license
[/PROMOTE]
```
The AI should emit `[PROMOTE]` blocks in the same message that answers or processes a question/check, so the fact appears in What we know simultaneously with the chat acknowledgment.
### 8.2 New marker: `[SUGGEST_FIX]`
```
[SUGGEST_FIX]
title: Clear cached credentials + rebuild Outlook profile
description: Stale cached credential in Credential Manager is holding the pre-reset token...
confidence: 94
script_template_slug: clear-outlook-credentials # or omitted if no template match
ai_drafted_script: | # only if no template match
# Generated by FlowPilot...
...
[/SUGGEST_FIX]
```
### 8.3 Removed markers
The old `[FORK]` marker from the ResolutionAssist prompt is removed. Forks were a Guided-mode concept; in the unified model, they're replaced by Questions with mutually exclusive answer options.
---
## 9. Implementation phases
Each phase ends with a git commit and verification step. Do not advance to the next phase until verification passes.
### Phase 0 — Prompt caching infrastructure (prerequisite)
A codebase audit revealed that prompt caching is only implemented in `assistant_chat_service.py` (the file being deprecated). Every other Anthropic API call site — including all of FlowPilot's 7 call sites through `AnthropicProvider` — is uncached. Phase 0 must land before Phase 2 starts because new services built in Phase 2 will inherit caching from `AnthropicProvider` automatically once it's fixed.
**Deliverables:**
- **0.1** Promote `AnthropicProvider.generate_json()` and `generate_text_stream()` in `ai_provider.py` to the cached pattern currently implemented in `assistant_chat_service.py:_call_anthropic_cached()`. Convert the `system` string parameter to a structured system block list with `cache_control: {"type": "ephemeral"}` on the static portion. Add a second breakpoint on the last history message. For the streaming variant, capture the final usage object via `get_final_message()`. Log `cache_read_input_tokens` and `cache_creation_input_tokens` on every response.
- **0.2** Update `integrations.py:557` (`/tickets/ai-parse`) to move the members list and team-stable boards data into a cached system block.
- **0.3** Add `cache_control` to one-shot generators: `ai_tree_generator`, `kb_conversion`, `ai_fix`, `script_builder`. Same pattern as 0.1.
- **0.4** Extract the caching logic from `assistant_chat_service.py:_call_anthropic_cached()` into `AnthropicProvider` and delete `_call_anthropic_cached`. `assistant_chat_service` should call the provider like every other service. This prevents two canonical implementations of the same pattern.
**Verification:**
- Hit any FlowPilot endpoint twice within 5 minutes. First call shows `cache_creation_input_tokens > 0`, second call shows `cache_read_input_tokens > 0`.
- If the second call returns zero cache reads, inspect the prefix for silent invalidators (timestamps, unsorted JSON keys, varying tool list ordering). Fix before proceeding.
```
git commit -m "feat(ai): promote AnthropicProvider to cached pattern, consolidate caching implementation"
```
**Dependencies:**
- Phase 1 (route rename and schema) can run in parallel with Phase 0.
- Phase 2 (What we know) must not start until Phase 0 is complete and verified.
### Phase 1 — Data model and route rename (backend + routing only)
**Deliverables:**
- Alembic migration creating `session_facts`, `session_suggested_fixes`, `draft_templates` tables and the column additions to `ai_sessions`, `script_templates`, `account_settings`
- All tables include `account_id` and have RLS policies following the multi-tenant architecture (per existing project standard)
- `/assistant``/pilot` route rename with permanent redirect (stays in place indefinitely; no sunset date)
- Sidebar nav entry rename
- No UI changes yet beyond the nav label
**Verification:**
- Run migration on a fresh dev database
- Confirm RLS policies active via the existing CI grep check for `tenant_filter()`
- Navigate to `/assistant` — should 301 to `/pilot`
- Navigate to `/pilot` — should render the existing ResolutionAssist UI with the sidebar entry now reading "FlowPilot"
```
git commit -m "feat(pilot): rename /assistant to /pilot, add session_facts + suggested_fixes + draft_templates schema"
```
### Phase 2 — What we know (task lane + service + API)
**Deliverables:**
- `FactSynthesisService` and its LLM prompt
- Fact CRUD API endpoints
- `WhatWeKnow`, `WhatWeKnowItem`, `AddNoteButton` components
- Task lane layout adjustment: What we know section renders above Questions
- Counter in task lane header updates to `X / Y answered` format
- AI prompt updated to emit `[PROMOTE]` markers; backend parses them and creates facts
**Verification:**
- Open a session, answer a question; within 2 seconds a fact should appear in What we know with correct provenance
- Click "+ Add a note", type a manual fact, confirm it appears with `source_type: user_note`
- Run a diagnostic check, confirm the check result promotes to a fact
- Facts persist across page reloads
- RLS: a user from a different account cannot read or write facts for this session
```
git commit -m "feat(pilot): add What we know section with fact synthesis"
```
### Phase 3 — Suggested fix + resolution note preview
**Deliverables:**
- `session_suggested_fixes` API endpoints and data flow
- `SuggestedFix` component in the task lane
- AI prompt updated to emit `[SUGGEST_FIX]` markers
- `ResolutionNoteGeneratorService` and preview endpoint
- `ResolutionNotePreview` floating popover anchored to Resolve button
- Preview refreshes on fact / suggested-fix changes (debounced)
**Verification:**
- Session with ≥3 facts and an active suggested fix shows a populated Resolve preview
- Editing a fact updates the preview within 1 second
- Preview markdown renders correctly with all four sections (Problem / What we confirmed / Root cause / Resolution)
- Preview contains no hallucinated information not present in session state (human review of 5 real-ish sessions)
```
git commit -m "feat(pilot): add suggested fix tracking and Resolve note preview"
```
### Phase 4 — Resolve and Escalate PSA writebacks
**Deliverables:**
- `transition_ticket_status` method with CW verification
- `post_resolution_note` endpoint and CW integration
- Resolve button fires: post note → transition status → mark session resolved → show templatize prompt (if applicable)
- `EscalationPackageGeneratorService` and parallel flow for Escalate
- Escalate button fires: post package → transition status → mark session escalated → route via CW rules
**Verification:**
- Complete a session end-to-end with a ConnectWise test instance
- Click Resolve, edit the preview, confirm post — verify the note appears in CW and status changes to Resolved
- Click Escalate on a different session — verify the package is posted and the ticket routes correctly
- Attempt to Resolve without a linked PSA ticket — should mark the session resolved without erroring, note stored in `resolution_note_markdown` only
```
git commit -m "feat(pilot): wire Resolve and Escalate to ConnectWise writeback"
```
### Phase 5 — Script Generator inline integration
**Deliverables:**
- `ScriptGenInline/TemplateMatchPanel` — when suggested fix has `script_template_id`, clicking the fix opens this panel with pre-filled parameters from session context
- Parameter pre-fill logic: pulls from session facts, ticket context (company configs), and AI-suggested values in the `[SUGGEST_FIX]` marker
- `ScriptGenInline/NoTemplateDialog` — three-option dialog when no template match
- User decision persisted on `session_suggested_fixes.user_decision`
- `TemplateExtractionService` for generating parameterization proposals
- Script generation flow produces a `script_generations` record linked to the session (existing Script Generator behavior)
**Verification:**
- Session with a template-matched suggested fix: clicking opens generator with ≥2 pre-filled parameters
- Session with a custom script suggested fix: dialog appears with three options, script preview shows parameters highlighted
- All three paths end correctly: one-off generates and closes, draft creates `draft_templates` row and generates, build_template opens full template creation
- `⌘K` → "script" anywhere in a session opens the generator directly
```
git commit -m "feat(pilot): integrate Script Generator inline with suggested fixes"
```
### Phase 6 — Post-resolve templatize prompt
**Deliverables:**
- `TemplatizePrompt` component
- Logic: after Resolve success, check for pending `draft_templates` rows for this session; if any, show the prompt
- Accept flow creates a new `script_templates` row with `source_session_id`, `source_user_id`, `source_ticket_ref` set
- "Don't ask me again" writes to `account_settings.templatize_prompt_enabled`
- Script Library sidebar shows a small badge when `draft_templates` with `status='pending'` exist for the current user
**Verification:**
- Resolve a session where the engineer picked Option 2 — templatize prompt appears with AI-proposed parameters
- Accept the prompt — new template appears in the Script Library with the provenance chip ("generated from CW #...")
- Skip the prompt — draft marked rejected, Script Library shows no new template
- Toggle "don't ask me again" — next session Resolve skips the prompt even with a pending draft
```
git commit -m "feat(pilot): add post-resolve templatize prompt for draft templates"
```
### Phase 7 — Polish
**Deliverables:**
- Visual polish against the mockup files (spacing, colors, animations)
- Loading states for LLM calls (fact synthesis, preview generation, template extraction)
- Empty states (new session with no facts yet, no active suggested fix, no draft templates pending)
- Keyboard shortcuts: `⌘K` (command menu), `⌘↵` (send composer), `⌘G` (generator), `⌘R` (resolve with confirm)
- Responsive behavior: task lane collapses on <1200px viewports into a bottom drawer
**Verification:**
- Compare each major screen side-by-side with the mockup PNG files — colors, spacing, typography within 5px / exact color match
- All flows work on a 1280px viewport without horizontal scroll
- Keyboard shortcuts documented in-app via `?` overlay
```
git commit -m "feat(pilot): visual polish and keyboard shortcuts"
```
---
## 10. Design system reference
All components must use the existing ResolutionFlow design system. Pulling the key tokens from the mockup CSS for quick reference — these should already exist in your tokens file; if they don't, add them:
```css
/* Backgrounds */
--bg-0: #070b12; /* page background */
--bg-1: #0d131c; /* sidebar / chrome */
--bg-2: #121a25; /* card / bubble background */
--bg-3: #1a2332; /* raised element */
/* Borders */
--border: rgba(148, 163, 184, 0.12);
--border-strong: rgba(148, 163, 184, 0.22);
/* Text */
--text-primary: #e2e8f0;
--text-secondary: #94a3b8;
--text-tertiary: #64748b;
/* Brand cyan (FlowPilot accent) */
--cyan-400: #22d3ee;
--cyan-500: #06b6d4;
--cyan-600: #0891b2;
--cyan-bg: rgba(34, 211, 238, 0.10);
--cyan-border: rgba(34, 211, 238, 0.30);
/* Semantic */
--success: #34d399; /* Resolve, facts */
--warning: #fbbf24; /* Escalate, proposed parameters */
--danger: #f87171;
--purple: #a78bfa; /* Script Generator / templates */
```
**Typography:**
- Body: IBM Plex Sans, 14px/1.5
- Headings: Bricolage Grotesque, 500 weight, -0.01em letter-spacing
- Code: JetBrains Mono
**Icons:** Phosphor Icons (Duotone) per the memory-recorded design decision to migrate off Lucide.
---
## 11. Non-goals for this migration
Do not build these as part of this work. They belong to later phases of the roadmap.
- **Confidence tiers (Discovery / Exploring / Guided).** We explicitly removed these. The task lane itself is the progress signal.
- **Mode toggle between Guided and Quick ask.** There is one mode.
- **"Convert to guided" promotion flow.** No longer applicable.
- **Team Wiki compilation from resolved sessions.** Tracked separately; depends on this migration but is not part of it.
- **SharePoint integration.** Sequenced after ConnectWise per roadmap.
- **Template marketplace / sharing across accounts.** Tracked under Client Context System roadmap item.
---
## 12. Risks and mitigations
| Risk | Mitigation |
|---|---|
| LLM fact synthesis hallucinates specifics not in the answer | Conservative prompt; engineer can edit/delete any AI-synthesized fact; provenance line shows the source so the engineer can verify |
| Resolution note preview LLM cost at scale | Cache aggressively, invalidate only on session state write; debounce UI updates to 500ms; consider lower-tier model for preview generation (final post-to-CW version can use the better model) |
| ConnectWise silently rejects status change | `transition_ticket_status` must re-fetch and verify; fail loudly if the change didn't stick |
| Template extraction proposes bad parameterization | Engineer reviews before saving; draft templates never silently become real templates; provenance chip lets team admins audit |
| Users lose muscle memory from `/assistant``/pilot` rename | Permanent redirect (no sunset date); inline toast on first `/pilot` visit explaining the rename |
| Existing sessions have no `session_facts` entries, so What we know is empty | Acceptable — Phase 2 deliberately does not backfill; facts only accumulate for new or ongoing sessions after deploy. Document in release notes. |
---
## 13. Questions for Michael before implementation starts
These are the decisions Claude Code cannot make unilaterally. Answer these inline in the doc or in chat before kicking off Phase 1.
1. **Keyboard shortcut for Resolve** — I've proposed `⌘R` (with a confirm). Browsers intercept `⌘R` for page reload. Alternative: `⌘⇧R` or no shortcut. Preference?
2. **Default `templatize_prompt_enabled` value** — I defaulted to `true`. If your beta testers find it annoying we'll learn fast, but it's a tradeoff between "every engineer sees the prompt" and "feature gets discovered only by those who know about it".
3. **Resolution note posts immediately, or stage for review?** — Current design: engineer edits preview inline, clicks Confirm & post. Alternative: stage in CW as draft note for a supervisor to approve before posting. Affects MSPs with strict compliance.
---
## End of document

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB