docs: review and improve Phase 4 plan, fix PSAProvider reference in CLAUDE.md

Phase 4 plan corrections:
- Remove shadcn/ui from tech stack (not used in project)
- Replace shadcn Sheet with Tailwind slide-up panel for mobile modals
- Specify exact CSS variable names for custom branding overrides
- Add SEO crawlability note (deferred to Phase 5)

Phase 4 plan enhancements:
- Add notification retry mechanism with NotificationLog model + APScheduler
- Specify weasyprint for PDF export with Dockerfile deps
- Add "Copy to Clipboard" as primary export option
- Add Task 6.5: in-app notification center extending existing NotificationsPanel
- Specify slowapi for IP-based public endpoint rate limiting
- Add low-priority gallery analytics tracking section (PostHog events)

CLAUDE.md: fix BasePsaProvider → PSAProvider (matches actual class name)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 05:50:45 +00:00
parent 9bad49d568
commit a8999adef3
2 changed files with 823 additions and 1 deletions

View File

@@ -199,7 +199,7 @@ Official ConnectWise developer guides live in `docs/connectwise/best-practices/`
- Auth: API Key auth (Base64 of `companyId+publicKey:privateKey`) + `clientId` header on every request
- `clientId` is server-side config (`CW_CLIENT_ID` in `config.py`) — identifies the ResolutionFlow app, NOT per-tenant. Per-connection credentials: `company_id`, `public_key`, `private_key`, `server_url`
- All PSA integration code in `services/psa/` — provider pattern with `BasePsaProvider` abstract class, `ConnectWiseProvider` implementation, `PsaProviderRegistry` for multi-PSA dispatch
- All PSA integration code in `services/psa/` — provider pattern with `PSAProvider` abstract base class, `ConnectWiseProvider` implementation, `PsaProviderRegistry` for multi-PSA dispatch
- PSA endpoints in `api/endpoints/psa_connections.py` — connection CRUD, ticket ops, member mapping
- Credentials encrypted at rest via `services/psa/encryption.py` (Fernet)
- Each MSP tenant provides their own CW credentials — ResolutionFlow stores these per-team, never per-user

View File

@@ -0,0 +1,822 @@
# FlowPilot-First Pivot — Phase 4: Growth, Polish & Enterprise Readiness
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Prepare ResolutionFlow for market growth and enterprise customers. Build the public templates gallery for SEO/lead-gen, add notification integrations (Slack/Teams/email) for escalation alerts, polish the mobile/responsive experience, add session export capabilities, and lay the groundwork for enterprise features (SSO prep, custom branding, multi-PSA).
**Architecture:** This phase is less about new core architecture and more about extending what exists. Templates gallery is a public-facing read-only layer over the existing flow/script template system. Notifications add webhook/event infrastructure. Mobile polish is a responsive design pass. Enterprise features add configuration surfaces.
**Tech Stack:** FastAPI, SQLAlchemy 2.0 (async), React, TypeScript, Tailwind CSS v4 (@tailwindcss/vite), Recharts, Resend (email notifications), weasyprint (PDF export), slowapi (rate limiting)
**Prerequisites:**
- Phase 1 complete (AI session core)
- Phase 2 complete (PSA integration, escalation handoff)
- Phase 3 complete (Knowledge Flywheel, Review Queue, in-session Script Generator, analytics)
- Existing models: `Tree`, `ScriptTemplate`, `AISession`, `FlowProposal`, `PsaConnection`
- Existing services: All Phase 1-3 services
- Existing frontend: All Phase 1-3 pages and components
**Pivot architecture doc:** `docs/ResolutionFlow_Pivot_Architecture.docx`
**Product strategy doc:** `docs/ResolutionFlow_Product_Strategy_Review.docx`
---
## Context: What Phase 4 Adds
Phases 1-3 built a complete AI-powered resolution engine with PSA integration, knowledge learning, and analytics. Phase 4 is about growth and polish — making the product ready for broader adoption and enterprise buyers.
**Public Templates Gallery:** A public-facing page at `/templates` (no auth required) that showcases curated flow templates and script templates. This is the primary SEO and lead-gen surface identified in the Product Strategy Review. Engineers discover ResolutionFlow by searching for troubleshooting guides, see the value, and sign up.
**Notification Integrations:** When a session is escalated, the receiving engineer (and team leads) should be notified via Slack, Microsoft Teams, or email. Also notify on: high-priority ticket sessions, Knowledge Flywheel proposals pending review, and session completion for tracked tickets.
**Session Export & Sharing:** Export session documentation as PDF or formatted text for sharing outside ResolutionFlow. "Generated with ResolutionFlow" branding on exports for viral growth (per Product Strategy Review).
**Mobile/Responsive Polish:** The FlowPilot session experience must work on tablets and phones. MSP techs often troubleshoot on-site with a tablet. This is a responsive design pass, not a native app.
**Enterprise Readiness:** Custom branding (logo, colors), SAML/SSO groundwork, multi-PSA support (Autotask, Halo PSA adapter stubs), and admin controls for AI model selection.
---
## Slice 1: Public Templates Gallery
### Task 1: Build public templates API (no auth required)
**Files:**
- Create: `backend/app/api/endpoints/public_templates.py`
- Create: `backend/app/schemas/public_templates.py`
**Schemas:**
```python
class PublicFlowTemplate(BaseModel):
"""A flow template visible in the public gallery."""
id: UUID
name: str
description: str | None
category: str | None
problem_domain: str | None
tree_type: str
step_count: int
usage_count: int
success_rate: float | None
tags: list[str]
preview_structure: dict[str, Any] # Simplified tree for preview (first 2-3 levels only)
created_at: datetime
class PublicScriptTemplate(BaseModel):
"""A script template visible in the public gallery."""
id: UUID
name: str
description: str | None
use_case: str | None
category_name: str
category_icon: str | None
complexity: str
tags: list[str]
parameter_count: int
requires_elevation: bool
requires_modules: list[str]
usage_count: int
is_verified: bool
created_at: datetime
class PublicGalleryResponse(BaseModel):
"""Combined gallery response."""
flow_templates: list[PublicFlowTemplate]
script_templates: list[PublicScriptTemplate]
total_flows: int
total_scripts: int
categories: list[str]
domains: list[str]
```
**Endpoints (NO authentication required):**
```
GET /api/v1/public/templates — Gallery listing (paginated, filterable)
GET /api/v1/public/templates/flows/{id} — Flow template detail (preview only, no full structure)
GET /api/v1/public/templates/scripts/{id} — Script template detail (preview only, no script body)
GET /api/v1/public/templates/categories — List all categories with counts
GET /api/v1/public/templates/search — Full-text search across flows + scripts
```
**Key implementation details:**
- Only expose flows/scripts marked as `visibility = "public"` or a new `is_gallery_featured` boolean
- Add `is_gallery_featured` boolean column to both `trees` and `script_templates` tables (migration required)
- `preview_structure` should be a truncated version of `tree_structure` — first 2-3 levels only, no deep branches. Don't expose the full flow to unauthenticated users — they need to sign up for that.
- Script templates in the gallery show parameter names and description but NOT the `script_body` — that's the value behind the signup wall
- Rate limit public endpoints via `slowapi` (standard FastAPI rate limiter): 30/minute per IP. The existing `core/rate_limit.py` is auth-based (per-user); public endpoints need IP-based limiting. Add `slowapi` to requirements and configure a `Limiter` instance with `get_remote_address` as the key function. Apply `@limiter.limit("30/minute")` to each public endpoint.
- Add OpenGraph meta tags support for social sharing (title, description, image)
- Cache responses aggressively (5-minute TTL) since public content changes infrequently
**SEO consideration:** These endpoints power the public page at `resolutionflow.com/templates`. The response should include fields that map well to structured data (schema.org HowTo or SoftwareApplication).
**Note (deferred):** React SPAs serve empty HTML to social media scrapers (Facebook, Slack, LinkedIn), so OpenGraph meta tags won't render in link previews. Options for Phase 5: prerendering service (prerender.io), build-time static HTML generation for gallery pages, or backend-side meta tag injection. Google's crawler handles JS fine, but social sharing will be limited until addressed.
**Verification:** Hit the public templates endpoint without auth. Verify flows and scripts with `is_gallery_featured = true` appear. Verify full script bodies and deep flow structures are NOT exposed.
```
git commit -m "feat(public): add public templates gallery API"
```
### Task 2: Build public templates gallery frontend
**Files:**
- Create: `frontend/src/pages/PublicTemplatesPage.tsx`
- Create: `frontend/src/components/public/TemplateGalleryGrid.tsx`
- Create: `frontend/src/components/public/FlowTemplateCard.tsx`
- Create: `frontend/src/components/public/ScriptTemplateCard.tsx`
- Create: `frontend/src/components/public/TemplateDetailModal.tsx`
- Create: `frontend/src/components/public/GallerySearch.tsx`
- Create: `frontend/src/api/publicTemplates.ts`
- Create: `frontend/src/types/public-templates.ts`
- Edit: `frontend/src/router.tsx`
**Route:** `/templates` — public, no auth required. Add to the router OUTSIDE the `ProtectedRoute` wrapper, alongside `/landing`, `/login`, `/register`.
**Design:** This is the first page many potential users will see. It needs to be visually impressive and clearly communicate value.
**Layout:**
**Hero section:**
- Heading: "MSP Troubleshooting Templates" (Bricolage Grotesque, large)
- Subheading: "Battle-tested flows and scripts built by MSP engineers. Free to browse, powerful when connected to FlowPilot."
- Search bar (prominent, centered)
- CTA: "Sign Up Free" button
**Filter bar:**
- Category pills: "All", "Active Directory", "Networking", "Microsoft 365", "Security", etc.
- Type toggle: "Flows", "Scripts", "All"
- Sort: "Most Used", "Newest", "Highest Success Rate"
**Grid:**
- Responsive card grid: 3 columns desktop, 2 tablet, 1 mobile
- Flow cards: name, description, domain badge, step count, success rate, usage count, "Preview" button
- Script cards: name, description, complexity badge (color-coded), verified badge, module tags, "View Details" button
**Template detail modal:**
- For flows: show the first 2-3 levels of the tree as a simplified visual (not the full flow editor — just a clean tree diagram). "Sign up to use this flow with FlowPilot" CTA.
- For scripts: show parameter list, description, use case, modules required. "Sign up to generate this script" CTA. Do NOT show the script body.
**Footer on every card:** "Powered by ResolutionFlow" with subtle branding
**Verification:** Open `/templates` without being logged in. Browse the gallery. Search for "Active Directory". Filter by scripts. Click a card — see the preview modal with signup CTA. Verify no sensitive data (script bodies, deep flow structures) is exposed.
```
git commit -m "feat(public): add public templates gallery page"
```
### Task 3: Admin curation tools for gallery
**Files:**
- Edit: `frontend/src/pages/admin/` (add gallery management section)
- Edit: `backend/app/api/endpoints/admin.py` (or create `admin_gallery.py`)
**What to add:**
Admin interface to manage which flows and scripts appear in the public gallery:
- Toggle `is_gallery_featured` on/off for any flow or script template
- Reorder gallery items (add `gallery_sort_order` column)
- Set gallery category overrides (a flow might have a different display category in the gallery vs internally)
- Preview how a template will look in the public gallery
**Verification:** Log in as admin. Feature a flow in the gallery. Open `/templates` in an incognito window. Verify the flow appears.
```
git commit -m "feat(admin): add gallery curation tools"
```
---
## Slice 2: Notification Integrations
### Task 4: Build notification event system
**Files:**
- Create: `backend/app/services/notification_service.py`
- Create: `backend/app/models/notification_config.py`
- Create: `backend/app/schemas/notification.py`
**Architecture:**
A lightweight event-driven notification system. Events are fired from various points in the codebase, and the notification service routes them to configured channels.
**Events:**
| Event | Trigger | Default Recipients |
|-------|---------|-------------------|
| `session.escalated` | Engineer escalates a session | Escalation target + team admins |
| `session.resolved` | Session resolved (if tracked) | Session owner |
| `proposal.pending` | Knowledge Flywheel creates proposal | Team admins |
| `proposal.approved` | Reviewer approves a proposal | Proposal source session engineer |
| `session.high_priority` | PSA ticket with high/critical priority starts a session | Team admins |
| `knowledge_gap.detected` | New high-severity knowledge gap identified | Team admins |
**Notification channels:**
- **Email** (via existing Resend integration — `settings.RESEND_API_KEY`)
- **Slack webhook** (simple `POST` to a webhook URL — no OAuth needed for v1)
- **Microsoft Teams webhook** (similar — Adaptive Card format)
**NotificationConfig model:**
```python
class NotificationConfig(Base):
__tablename__ = "notification_configs"
id: UUID (PK)
account_id: UUID (FK)
channel: str # "email" | "slack_webhook" | "teams_webhook"
webhook_url: str | None # For Slack/Teams
is_active: bool
events_enabled: JSONB # {"session.escalated": true, "proposal.pending": true, ...}
created_at: datetime
updated_at: datetime
```
**Service pattern:**
```python
async def notify(event: str, account_id: UUID, payload: dict, db: AsyncSession):
"""Fire a notification event. Routes to all active channels for this account."""
configs = await _get_active_configs(account_id, event, db)
for config in configs:
if config.channel == "email":
await _send_email_notification(event, payload, config)
elif config.channel == "slack_webhook":
await _send_slack_notification(event, payload, config)
elif config.channel == "teams_webhook":
await _send_teams_notification(event, payload, config)
```
**Key details:**
- Run notifications async (don't block the response) via `asyncio.create_task()`
- Slack format: Rich message with fields (session summary, escalation reason, link to session)
- Teams format: Adaptive Card with similar fields
- Email format: Simple HTML email using Resend (reuse existing email infrastructure)
- On webhook failure: log to `notification_logs` table and schedule retry via APScheduler (exponential backoff: 30s, 2m, 10m — max 3 retries). Same pattern as PSA push retry from Phase 2.
**NotificationLog model:**
```python
class NotificationLog(Base):
__tablename__ = "notification_logs"
id: UUID (PK)
notification_config_id: UUID (FK notification_configs.id)
event: str # "session.escalated", "proposal.pending", etc.
payload: JSONB # Event payload snapshot
status: str # "sent" | "failed" | "retrying" | "exhausted"
retry_count: int (default 0)
last_error: str | None
next_retry_at: datetime | None
created_at: datetime
delivered_at: datetime | None
```
**Verification:** Configure a Slack webhook. Escalate a session. Verify Slack message appears with session summary and link.
```
git commit -m "feat(notifications): add notification event system with email/Slack/Teams"
```
### Task 5: Wire notifications into session lifecycle
**Files:**
- Edit: `backend/app/services/flowpilot_engine.py`
- Edit: `backend/app/services/knowledge_flywheel.py`
- Edit: `backend/app/api/endpoints/flow_proposals.py`
**What to add:**
Add `notify()` calls at these points:
- `flowpilot_engine.escalate_session()` → fire `session.escalated`
- `flowpilot_engine.start_session()` → if PSA ticket has high/critical priority → fire `session.high_priority`
- `knowledge_flywheel.analyze_session()` → when creating a `pending` proposal → fire `proposal.pending`
- `flow_proposals.review_proposal()` → when approving → fire `proposal.approved`
All notification calls should use `asyncio.create_task()` to avoid blocking.
**Verification:** Configure email notifications. Escalate a session. Check email inbox. Configure Slack webhook. Create a high-priority ticket session. Verify Slack notification fires.
```
git commit -m "feat(notifications): wire notifications into session and proposal lifecycle"
```
### Task 6: Notification settings UI
**Files:**
- Create: `frontend/src/components/account/NotificationSettings.tsx`
- Edit: `frontend/src/pages/account/IntegrationsPage.tsx`
- Create: `frontend/src/api/notifications.ts`
- Create: `frontend/src/types/notification.ts`
**What to add:**
Under Account Settings → Integrations, add a "Notifications" section:
- **Email notifications:** Toggle per event type. Uses the account owner's email by default. Option to add additional email addresses.
- **Slack:** "Connect Slack" → paste webhook URL → test button → configure which events to send
- **Microsoft Teams:** Same pattern — paste webhook URL → test → configure events
Each channel shows a card with:
- Channel name + icon (Slack logo, Teams logo, email icon)
- Status indicator (active/inactive)
- Event toggles (checkboxes for each event type)
- Test button → sends a test notification to verify the webhook works
- Remove button
**Verification:** Open integrations page. Add a Slack webhook. Toggle escalation events on. Click test. Verify test message appears in Slack. Escalate a session. Verify real notification appears.
```
git commit -m "feat(notifications): add notification settings UI"
```
### Task 6.5: In-app notification center
**Files:**
- Edit: `frontend/src/components/layout/NotificationsPanel.tsx` (EXISTING — currently shows recent session activity only)
- Create: `backend/app/api/endpoints/notifications.py`
- Create: `backend/app/models/notification.py`
- Edit: `backend/app/api/router.py`
**Context:** An existing `NotificationsPanel` component already renders a bell icon with a dropdown in the top bar. It currently fetches recent sessions via `sessionsApi.list()` and shows them as an activity feed. This task extends it into a real notification center.
**Backend — Notification model:**
```python
class Notification(Base):
__tablename__ = "notifications"
id: UUID (PK)
account_id: UUID (FK)
user_id: UUID (FK users.id) # Recipient
event: str # "session.escalated", "proposal.pending", etc.
title: str # "Session escalated by John"
body: str | None # Brief summary
link: str | None # In-app link, e.g. "/pilot/{session_id}"
is_read: bool (default False)
created_at: datetime
```
**Backend endpoints:**
```
GET /api/v1/notifications — List notifications (paginated, unread first)
GET /api/v1/notifications/unread-count — Unread count (for badge)
PATCH /api/v1/notifications/{id}/read — Mark as read
POST /api/v1/notifications/mark-all-read — Mark all as read
```
**Frontend changes to `NotificationsPanel.tsx`:**
- Replace `sessionsApi.list()` with notifications API
- Show unread count badge on the bell icon (red dot → count badge when > 0)
- Each notification item: icon (by event type), title, body, time ago, link
- Click a notification → mark as read + navigate to the link
- "Mark all as read" button in the dropdown header
- Keep existing glass-card dropdown styling
**Wire into notification service:** When `notify()` fires for email/Slack/Teams, also create a `Notification` row for each target user. In-app notifications are always created regardless of channel config.
**Verification:** Escalate a session. Check the bell icon shows a badge. Open dropdown — see the escalation notification. Click it — navigate to the session. Badge clears.
```
git commit -m "feat(notifications): add in-app notification center extending existing NotificationsPanel"
```
---
## Slice 3: Session Export & Sharing
### Task 7: Build session export service
**Files:**
- Create: `backend/app/services/session_export_service.py`
- Edit: `backend/app/api/endpoints/ai_sessions.py`
**Export formats:**
**1. PDF export:**
- Professional PDF with ResolutionFlow branding
- Sections: Problem Summary, Diagnostic Trail, Resolution/Escalation, Session Metadata
- Each step formatted cleanly with numbered steps, responses, outcomes
- "Generated with ResolutionFlow" footer + URL on every page
- Redact sensitive data (passwords) using existing `redaction_service.py`
**2. Markdown export:**
- Clean markdown version of the session documentation
- Suitable for pasting into wikis, Confluence, SharePoint, etc.
- Same structure as PDF but in markdown format
**3. Shareable link:**
- Generate a time-limited shareable link (24h default, configurable)
- Reuse the existing `SessionShare` model pattern from legacy sessions
- Public view shows the session in read-only mode with minimal UI
- "Generated with ResolutionFlow — Start your free trial" CTA at the bottom
**New endpoints:**
```
GET /api/v1/ai-sessions/{id}/export?format=pdf — Download PDF
GET /api/v1/ai-sessions/{id}/export?format=markdown — Download markdown
POST /api/v1/ai-sessions/{id}/share — Create shareable link
GET /api/v1/shared/ai-session/{token} — Public view (no auth)
```
**PDF generation:** Use `weasyprint` — it produces the best output for HTML→PDF conversion. Requires system-level dependencies in the Dockerfile:
```dockerfile
# Add to backend/Dockerfile before pip install
RUN apt-get update && apt-get install -y --no-install-recommends \
libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf2.0-0 libffi-dev \
&& rm -rf /var/lib/apt/lists/*
```
Generate a styled HTML template (with ResolutionFlow branding, fonts, colors) and pass it to `weasyprint.HTML(string=html).write_pdf()`. Keep the HTML template in `backend/app/templates/session_export.html`.
**Key detail — growth mechanic:** Every export and shared link includes "Generated with ResolutionFlow" branding. This is the viral growth loop identified in the Product Strategy Review. Make the branding tasteful but visible — a small footer, not an intrusive watermark.
**Verification:** Resolve a session. Export as PDF — verify clean formatting with branding. Export as markdown — verify proper markdown. Create a share link — open in incognito — verify read-only view with CTA.
```
git commit -m "feat(export): add session export (PDF, markdown, shareable link)"
```
### Task 8: Export and share buttons in frontend
**Files:**
- Create: `frontend/src/components/flowpilot/SessionExportMenu.tsx`
- Edit: `frontend/src/components/flowpilot/SessionDocView.tsx`
- Edit: `frontend/src/components/flowpilot/FlowPilotSession.tsx`
**What to add:**
An export menu in the session documentation view and completed session view:
- "Export" dropdown button with options: "Download PDF", "Download Markdown", "Copy to Clipboard" (formatted text via `navigator.clipboard.writeText()` — most-used option for pasting into PSA ticket notes, wikis, or emails)
- "Share" button → generates shareable link → copies to clipboard with toast notification
- Share link shows expiration time and option to revoke
**Design:** Small dropdown menu, consistent with existing UI patterns. Export icon from Lucide (`Download` or `Share2`).
**Verification:** Complete a session. Click Export → Download PDF. Verify PDF downloads. Click Share → verify link is copied. Open link in incognito.
```
git commit -m "feat(export): add export and share UI"
```
---
## Slice 4: Mobile/Responsive Polish
### Task 9: Responsive design pass for FlowPilot session
**Files:**
- Edit: `frontend/src/components/flowpilot/FlowPilotIntake.tsx`
- Edit: `frontend/src/components/flowpilot/FlowPilotSession.tsx`
- Edit: `frontend/src/components/flowpilot/FlowPilotStepCard.tsx`
- Edit: `frontend/src/components/flowpilot/FlowPilotOptions.tsx`
- Edit: `frontend/src/components/flowpilot/FlowPilotActionBar.tsx`
- Edit: `frontend/src/components/flowpilot/ConfidenceIndicator.tsx`
- Edit: `frontend/src/components/flowpilot/SessionDocView.tsx`
- Edit: `frontend/src/components/flowpilot/EscalateModal.tsx`
- Edit: `frontend/src/components/flowpilot/EscalationQueue.tsx`
- Edit: `frontend/src/components/flowpilot/SessionTicketCard.tsx`
- Edit: `frontend/src/components/flowpilot/InSessionScriptGenerator.tsx`
- Edit: `frontend/src/pages/ReviewQueuePage.tsx`
- Edit: `frontend/src/pages/FlowPilotAnalyticsPage.tsx`
- Edit: `frontend/src/pages/PublicTemplatesPage.tsx`
**This is NOT a rebuild. It's a responsive audit and fix pass.**
**Breakpoints (follow existing Tailwind v4 breakpoints):**
- Mobile: < 640px (`sm:`)
- Tablet: 640px - 1024px (`md:`)
- Desktop: > 1024px (`lg:`)
**Key responsive changes:**
**FlowPilot Session (most critical):**
- Desktop: Two-column layout (conversation 70% + sidebar 30%)
- Tablet: Sidebar collapses to a top bar with key info (problem summary, confidence, ticket #). Expandable on tap.
- Mobile: Single column. Sidebar info moves to a collapsible header. Action bar stays fixed at bottom.
**Options Grid:**
- Desktop: 2-column grid
- Tablet: 2-column grid (slightly narrower cards)
- Mobile: Single column stack
**Step Cards:**
- Desktop/Tablet: Full width with padding
- Mobile: Edge-to-edge cards with reduced padding
**Action Bar (Resolve/Escalate):**
- All sizes: Fixed to bottom of viewport. Buttons full-width on mobile.
**Intake Screen:**
- All sizes: Already mostly responsive (centered card). Ensure textarea and buttons are full-width on mobile.
**Modals (Resolve, Escalate, Ticket Picker):**
- Desktop: Centered modal with max-width
- Mobile: Full-screen slide-up panel built with Tailwind (`fixed inset-x-0 bottom-0 h-[90vh] rounded-t-2xl` with `translate-y` animation), or full-width modal matching existing modal patterns
**Script Generator (in-session):**
- Desktop: Inline with side-by-side form + preview
- Mobile: Stacked — form on top, preview below. Preview uses horizontal scroll for long script lines.
**Review Queue:**
- Desktop: Two-panel (list + detail)
- Mobile: List only, tap to navigate to detail page
**Analytics Dashboard:**
- Desktop: Multi-column chart grid
- Mobile: Single column, charts stack vertically. Charts resize to viewport width.
**Public Templates Gallery:**
- Desktop: 3-column card grid
- Tablet: 2-column
- Mobile: Single column
**Testing approach:**
- Use Chrome DevTools device emulation for iPhone 14 Pro (390px), iPad (810px), and desktop (1440px)
- Test every FlowPilot page at each breakpoint
- Ensure no horizontal overflow, no text truncation that hides critical info, all buttons are tappable (minimum 44px touch targets)
**Verification:** Open FlowPilot on Chrome DevTools mobile emulation. Start a session. Progress through steps. Resolve. Verify the entire flow is usable on a phone-sized screen. Repeat on tablet size.
```
git commit -m "feat(responsive): mobile/tablet responsive pass for all FlowPilot pages"
```
---
## Slice 5: Enterprise Readiness Foundations
### Task 10: Custom branding system
**Files:**
- Edit: `backend/app/models/account.py` (or use existing branding model if it exists)
- Edit: `backend/app/api/endpoints/branding.py` (existing endpoint)
- Edit: `frontend/src/components/layout/AppLayout.tsx`
- Edit: `frontend/src/styles/` (CSS variable overrides)
**What to add:**
Check if branding infrastructure already exists (there's a `branding.py` endpoint and the Product Strategy mentions custom branding for Enterprise). If it does, extend it. If not, build:
- Account-level branding settings: logo URL, primary color, sidebar color, company name
- CSS variable overrides applied at the layout level via inline `style` on the app shell root. Target the exact Tailwind v4 theme variables from `index.css`:
- `--color-primary` (oklch — main accent, currently cyan `oklch(0.65 0.13 195)`)
- `--color-primary-foreground` (text on primary backgrounds)
- `--glass-bg` (card/panel backgrounds)
- Convert the customer's hex color to oklch using a utility function before applying
- Logo appears in the sidebar header, replacing the ResolutionFlow logo
- Company name appears in session exports and shared links
- Branding settings page in Account Settings (owner-only)
**Keep it simple for v1:** Just logo + primary accent color + company name. Full theme customization can come later.
**Verification:** Upload a logo and set a custom primary color. Verify the sidebar shows the custom logo. Verify the accent color changes throughout the app. Export a session — verify company name appears.
```
git commit -m "feat(enterprise): add custom branding system"
```
### Task 11: Multi-PSA adapter stubs
**Files:**
- Create: `backend/app/services/psa/autotask/` directory with `__init__.py`, `provider.py`, `client.py`
- Create: `backend/app/services/psa/halopsa/` directory with `__init__.py`, `provider.py`, `client.py`
- Edit: `backend/app/services/psa/registry.py`
**What to add:**
Create stub implementations for Autotask and Halo PSA that extend the existing `PSAProvider` abstract base class. These stubs should:
- Implement all abstract methods
- Raise `NotImplementedError("Autotask integration coming soon")` for each method
- Include docstrings noting the expected API endpoints and authentication patterns
- Register in the PSA registry so the provider selection dropdown shows them as "Coming Soon"
**Why stubs now:** This establishes the multi-PSA architecture so when it's time to fully implement Autotask or Halo, the structure is ready. It also signals to enterprise prospects that multi-PSA is on the roadmap.
**Frontend change:** In the PSA connection setup UI, show Autotask and Halo PSA as options with a "Coming Soon" badge. They should be visible but not selectable.
**Verification:** Open the integrations page. See ConnectWise (active), Autotask (Coming Soon badge), Halo PSA (Coming Soon badge).
```
git commit -m "feat(enterprise): add multi-PSA adapter stubs for Autotask and Halo"
```
### Task 12: SSO/SAML groundwork
**Files:**
- Create: `backend/app/services/sso_service.py` (stub)
- Edit: `backend/app/models/account.py`
**What to add:**
This is groundwork only — NOT a full SSO implementation. Add:
- `sso_enabled` boolean on the Account model (default false)
- `sso_provider` string on Account (nullable — "saml" | "oidc" | null)
- `sso_config` JSONB on Account (nullable — will hold IdP metadata URL, entity ID, etc.)
- A stub `sso_service.py` with the expected interface:
```python
async def initiate_sso_login(account_slug: str) -> str: ... # Returns redirect URL
async def process_sso_callback(saml_response: str) -> User: ... # Returns authenticated user
async def validate_sso_config(config: dict) -> bool: ... # Tests IdP connectivity
```
- Migration for the new columns
**Do NOT implement the actual SAML/OIDC flow.** This just establishes the model and service interface so when an enterprise customer needs it, the architecture is ready and the feature flag can be checked.
**Frontend:** In Account Settings, show an "SSO / Single Sign-On" section with a "Contact us to enable SSO for your organization" message. This acts as a lead capture for enterprise sales conversations.
**Verification:** Check the account model has SSO fields. Verify the settings page shows the SSO section with the contact message.
```
git commit -m "feat(enterprise): add SSO/SAML groundwork (model + stub service)"
```
---
## Summary of All New/Modified Files
### Backend — New
```
app/api/endpoints/public_templates.py # Public gallery API
app/schemas/public_templates.py # Public gallery schemas
app/services/notification_service.py # Event-driven notifications (with retry via APScheduler)
app/models/notification_config.py # Notification channel configs
app/models/notification_log.py # Notification delivery log (retry tracking)
app/models/notification.py # In-app notification model
app/api/endpoints/notifications.py # In-app notification endpoints
app/schemas/notification.py # Notification schemas
app/templates/session_export.html # Styled HTML template for PDF export
app/services/session_export_service.py # PDF/markdown/share export
app/services/psa/autotask/__init__.py # Autotask stub
app/services/psa/autotask/provider.py # Autotask provider stub
app/services/psa/autotask/client.py # Autotask client stub
app/services/psa/halopsa/__init__.py # Halo PSA stub
app/services/psa/halopsa/provider.py # Halo PSA provider stub
app/services/psa/halopsa/client.py # Halo PSA client stub
app/services/sso_service.py # SSO stub service
alembic/versions/xxx_phase4_growth.py # Migration
```
### Backend — Modified
```
app/api/router.py # Register new routers
app/api/endpoints/ai_sessions.py # Export + share endpoints
app/api/endpoints/branding.py # Custom branding extensions
app/services/flowpilot_engine.py # Notification calls on escalation/high-priority
app/services/knowledge_flywheel.py # Notification calls on proposal creation
app/api/endpoints/flow_proposals.py # Notification calls on approval
app/services/psa/registry.py # Register Autotask + Halo stubs
app/models/account.py # SSO fields
app/models/tree.py # is_gallery_featured, gallery_sort_order
app/models/script_template.py # is_gallery_featured, gallery_sort_order
```
### Frontend — New
```
src/pages/PublicTemplatesPage.tsx # Public gallery page
src/components/public/TemplateGalleryGrid.tsx # Gallery grid layout
src/components/public/FlowTemplateCard.tsx # Flow card
src/components/public/ScriptTemplateCard.tsx # Script card
src/components/public/TemplateDetailModal.tsx # Preview modal with signup CTA
src/components/public/GallerySearch.tsx # Search component
src/components/flowpilot/SessionExportMenu.tsx # Export dropdown
src/components/account/NotificationSettings.tsx # Notification channel config
src/api/publicTemplates.ts # Public gallery API client
src/api/notifications.ts # Notifications API client
src/types/public-templates.ts # Public gallery types
src/types/notification.ts # Notification types
```
### Frontend — Modified (responsive pass)
```
src/components/flowpilot/FlowPilotIntake.tsx
src/components/flowpilot/FlowPilotSession.tsx
src/components/flowpilot/FlowPilotStepCard.tsx
src/components/flowpilot/FlowPilotOptions.tsx
src/components/flowpilot/FlowPilotActionBar.tsx
src/components/flowpilot/ConfidenceIndicator.tsx
src/components/flowpilot/SessionDocView.tsx
src/components/flowpilot/EscalateModal.tsx
src/components/flowpilot/EscalationQueue.tsx
src/components/flowpilot/SessionTicketCard.tsx
src/components/flowpilot/InSessionScriptGenerator.tsx
src/pages/ReviewQueuePage.tsx
src/pages/FlowPilotAnalyticsPage.tsx
src/pages/PublicTemplatesPage.tsx
src/router.tsx # Public gallery route + any new routes
src/components/sidebar/ # Updated with any new nav entries
src/components/layout/AppLayout.tsx # Custom branding support
src/pages/account/IntegrationsPage.tsx # Notification settings + PSA stubs
```
---
## Database Changes
**Migration:** Single migration with multiple changes:
```python
# 1. Gallery featuring columns
op.add_column('trees', sa.Column('is_gallery_featured', sa.Boolean(), nullable=True, server_default='false'))
op.add_column('trees', sa.Column('gallery_sort_order', sa.Integer(), nullable=True, server_default='0'))
op.add_column('script_templates', sa.Column('is_gallery_featured', sa.Boolean(), nullable=True, server_default='false'))
op.add_column('script_templates', sa.Column('gallery_sort_order', sa.Integer(), nullable=True, server_default='0'))
# 2. Notification configs table
op.create_table('notification_configs', ...)
# 3. Notification logs table (retry tracking)
op.create_table('notification_logs', ...)
# 4. In-app notifications table
op.create_table('notifications', ...)
# 5. SSO groundwork on accounts
op.add_column('accounts', sa.Column('sso_enabled', sa.Boolean(), nullable=True, server_default='false'))
op.add_column('accounts', sa.Column('sso_provider', sa.String(20), nullable=True))
op.add_column('accounts', sa.Column('sso_config', sa.dialects.postgresql.JSONB(), nullable=True))
```
**Run migration:**
```bash
cd /projects/patherly/backend
DATABASE_URL=postgresql://postgres:postgres@resolutionflow_postgres:5432/resolutionflow \
venv/bin/alembic upgrade head
```
---
## Testing Strategy
### Backend Tests
**Files:** `backend/tests/test_public_templates.py`
- Test gallery endpoint returns only featured templates
- Test no script bodies exposed
- Test no deep flow structures exposed
- Test search functionality
- Test unauthenticated access works
**Files:** `backend/tests/test_notification_service.py`
- Test event routing to correct channels
- Test Slack webhook format
- Test Teams webhook format
- Test email notification
- Test disabled events are not sent
**Files:** `backend/tests/test_session_export.py`
- Test PDF generation
- Test markdown generation
- Test shareable link creation and expiry
- Test branding in exports
### Frontend Manual Testing
1. Open `/templates` without login — browse gallery, search, filter
2. Click a template — see preview modal with signup CTA
3. Configure Slack notifications — escalate a session — verify Slack message
4. Export a session as PDF — verify clean formatting + branding
5. Share a session — open link in incognito — verify read-only view
6. Test all FlowPilot pages on mobile viewport (390px)
7. Test all FlowPilot pages on tablet viewport (810px)
8. Upload custom branding — verify it appears throughout the app
9. Check integrations page — verify Autotask and Halo show as "Coming Soon"
10. Escalate a session — verify bell icon badge appears — click notification — navigates to session
---
## Low Priority: Gallery Analytics Tracking
PostHog is already integrated. When building the public gallery (Slice 1), add these events using `analytics.ts` helpers:
- `gallery_viewed` — page load with active filters/category
- `template_clicked` — which template, flow vs script, position in grid
- `template_search` — search terms (valuable for understanding MSP needs)
- `signup_cta_clicked` — from gallery detail modal (conversion tracking)
This data reveals which templates drive signups and what topics to create more content around. Implement alongside Tasks 1-2, not as a separate task.
---
## What Comes Next (Phase 5+ — Future)
- **Full Autotask implementation:** Complete the PSA provider for Autotask PSA
- **Full Halo PSA implementation:** Complete the PSA provider for Halo PSA
- **Full SSO/SAML implementation:** Complete SAML + OIDC authentication flows
- **Native mobile app:** React Native or PWA for on-site troubleshooting
- **AI model selection per account:** Allow enterprise customers to choose model tier (haiku/sonnet/opus)
- **Webhook API:** Public webhooks for third-party integrations
- **ConnectWise Marketplace listing:** Package and submit to CW marketplace
- **White-label option:** Full branding removal for enterprise resellers