Files
resolutionflow/docs/2026-03-18-flowpilot-first-pivot-phase4.md
chihlasm 897cfbc1e4 docs: update Phase 4 plan with completion status for Slices 2 and 3
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>
2026-03-19 18:55:32 +00:00

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.


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 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"

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"

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 — 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 POST to 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_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:

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:

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 on SessionDetailPage (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 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:

# 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:
    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

  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

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