Slice 2 (Notifications) fully complete. Slice 3 (Session Export) ~80% done, needs polish only. Remaining execution order: Slice 1 → 3 polish → 4 → 5. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
38 KiB
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.
Status (2026-03-19): Slice 2 (Notifications) COMPLETE. Slice 3 (Session Export) ~80% COMPLETE (needs polish only). Remaining: Slices 1, 4, 5. Execute in order: Slice 1 → Slice 3 polish → Slice 4 → Slice 5.
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:
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 newis_gallery_featuredboolean - Add
is_gallery_featuredboolean column to bothtreesandscript_templatestables (migration required) preview_structureshould be a truncated version oftree_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 existingcore/rate_limit.pyis auth-based (per-user); public endpoints need IP-based limiting. Addslowapito requirements and configure aLimiterinstance withget_remote_addressas 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 createadmin_gallery.py)
What to add:
Admin interface to manage which flows and scripts appear in the public gallery:
- Toggle
is_gallery_featuredon/off for any flow or script template - Reorder gallery items (add
gallery_sort_ordercolumn) - 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 — COMPLETE (2026-03-19)
Status: Fully implemented in Phase 4 Slice 2 session. Models, service, API endpoints, in-app notifications, retry scheduler, frontend panel and settings all done. Skip to Slice 3.
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
POSTto a webhook URL — no OAuth needed for v1) - Microsoft Teams webhook (similar — Adaptive Card format)
NotificationConfig model:
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:
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_logstable and schedule retry via APScheduler (exponential backoff: 30s, 2m, 10m — max 3 retries). Same pattern as PSA push retry from Phase 2.
NotificationLog model:
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()→ firesession.escalatedflowpilot_engine.start_session()→ if PSA ticket has high/critical priority → firesession.high_priorityknowledge_flywheel.analyze_session()→ when creating apendingproposal → fireproposal.pendingflow_proposals.review_proposal()→ when approving → fireproposal.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:
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 — MOSTLY COMPLETE (polish only)
Status:
export_service.py(1145 lines, 5 formats), frontend export UI onSessionDetailPage(format selection, preview, clipboard, download, redaction), and WeasyPrint are all implemented. Remaining: verify PDF template styling, add loading spinner for PDF generation, ensure "Generated with ResolutionFlow" branding on all formats, end-to-end test all formats.
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
SessionSharemodel 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:
# 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-2xlwithtranslate-yanimation), 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
styleon the app shell root. Target the exact Tailwind v4 theme variables fromindex.css:--color-primary(oklch — main accent, currently cyanoklch(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_enabledboolean on the Account model (default false)sso_providerstring on Account (nullable — "saml" | "oidc" | null)sso_configJSONB on Account (nullable — will hold IdP metadata URL, entity ID, etc.)- A stub
sso_service.pywith the expected interface: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:
# 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:
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
- Open
/templateswithout login — browse gallery, search, filter - Click a template — see preview modal with signup CTA
- Configure Slack notifications — escalate a session — verify Slack message
- Export a session as PDF — verify clean formatting + branding
- Share a session — open link in incognito — verify read-only view
- Test all FlowPilot pages on mobile viewport (390px)
- Test all FlowPilot pages on tablet viewport (810px)
- Upload custom branding — verify it appears throughout the app
- Check integrations page — verify Autotask and Halo show as "Coming Soon"
- 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/categorytemplate_clicked— which template, flow vs script, position in gridtemplate_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