Files
resolutionflow/CLAUDE.md
chihlasm ff985fb755 refactor: replace cyan accent with ember orange across entire frontend
Swap accent color from cyan (#22d3ee) to ember orange (#f97316) site-wide.
Cyan caused contrast issues and felt generic — orange brings warmth and
urgency fitting for a troubleshooting tool.

Changes:
- CSS variables: accent, accent-hover, accent-dim, accent-text, primary, ring
- Warning color shifted from amber (#fbbf24) to yellow (#eab308) for
  semantic separation from orange accent
- Brand SVGs: logo gradient updated to orange
- 50+ component files: all hardcoded cyan hex values, Tailwind cyan-*
  classes, and rgba(34,211,238,...) glow values replaced
- landing.css: all 45+ cyan references + 5 old border color fixes
- DESIGN-SYSTEM.md bumped to v5 with decisions log
- CLAUDE.md: all color references synced to charcoal palette + orange accent
- PWA theme-color meta tag updated to match sidebar (#10121a)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 07:37:44 +00:00

38 KiB
Raw Blame History

CLAUDE.md - Patherly / ResolutionFlow Project Context

Last Updated: March 23, 2026


Project Overview

Patherly (user-facing brand: ResolutionFlow) is a SaaS product for MSP professionals. It provides troubleshooting decision trees that guide engineers through proven troubleshooting paths, capture decisions and notes, and generate professional ticket documentation.

Target Market: MSP companies — IT service providers managing infrastructure and support for multiple clients.

SaaS Context: Multi-tenant design — teams represent MSP companies, trees shared within teams, tiered access (super_admin, team_admin, engineer, viewer).

Branding

Context Name Used
Repository / directory / database / Docker patherly / patherly_postgres
Backend, frontend UI, production URLs ResolutionFlow
  • Design system: DESIGN-SYSTEM.md — THE source of truth for all design decisions
  • Design aesthetic: Flat, high-contrast dark theme (Sentry/PostHog-inspired). No glass morphism, no gradients on surfaces, no ambient effects. Light mode planned.
  • Accent color: Ember orange (#f97316 / #ea580c). Used sparingly — ≤5% of the UI. Warning is yellow (#eab308), not amber, to stay distinct from accent.
  • Fonts: IBM Plex Sans (font-sans, body), Bricolage Grotesque (font-heading, headings), JetBrains Mono (font-mono, code) — loaded via Google Fonts
  • Logo: 30px gradient square (cyan) + "ResolutionFlow" in Bricolage Grotesque 700
  • Layout: Icon rail sidebar (72px default) with hover flyout panels. Pinnable to full 260px sidebar. See DESIGN-SYSTEM.md
  • Brand assets: brand-assets/ (source SVGs), frontend/src/assets/brand/ (app assets), frontend/public/icons/ (favicon)
  • Terminology: User-facing label is "Flows" (not "Trees"). Procedural flows are called "Projects" in the UI. Step Library is called "Solutions Library" in the UI. Maintenance flows are hidden from UI for pilot (backend still supports them). tree_type column values unchanged in DB.
  • Reference mockups: docs/mockups/ (HTML files, open in browser)

Component styling rules:

  • Primary buttons: solid accent background (#f97316), white text, 5px radius
  • Ghost buttons: transparent with 1px border-default, hover bg-elevated
  • Cards: bg-card with 1px border-default, 8px radius. NO shadows, NO blur, NO gradients.
  • Badges: pill-shaped (20px radius), semantic dim background + matching text color
  • Active nav: accent-dim background + accent-text color + 3px left accent bar
  • Stat cards: 3px colored left border (accent/success/warning by position)
  • Code blocks: bg-code with JetBrains Mono, material-inspired syntax highlighting
  • Status colors: green/#34d399 (success), yellow/#eab308 (warning), red/#f87171 (danger) — ONLY for semantic meaning
  • Section labels: 10px, 600 weight, uppercase, text-muted, 1.2px letter-spacing

When adding new pages/components: reference DESIGN-SYSTEM.md. Use flat dark surfaces, 1px borders, no decorative effects. All colors via CSS variables. Use "Flows" not "Trees" in all user-facing text; use "Projects" not "Procedures" for procedural flows.

Implementation Principles

  • Prefer correct architecture over minimal diff
  • If two approaches exist, implement the one that scales, not the one that's faster to write
  • Flag any "simpler approach" tradeoffs for product owner review before proceeding

Current State

  • Phase: Go-to-Market Validation (Pre-PMF)
  • Backend: Complete (35+ API endpoints, 100+ integration tests)
  • Frontend: Core features complete, Tree Editor functional
  • Database: PostgreSQL with Docker, 75 migrations
  • Detailed status: CURRENT-STATE.md

What's In Progress

  • GTM validation: Shadow & Ship — founder dogfooding for 2 weeks, then 5 colleague pilot
  • Solutions Library spec written (docs/plans/2026-03-23-solutions-library-design.md), implementation post-pilot
  • Remaining open issues: #66 Templates + Import/Export, #60 Recurring Issue Detection, #58 Step Feedback Flag

Recently Completed

  • Copilot-first dashboard redesign: ChatGPT-style input, suggestion chips, simplified sidebar
  • Charcoal color palette: sidebar-darkest approach (#10121a sidebar, #1a1c23 page, #22252e cards)
  • Unified Command Palette: merged QuickLaunch into omnibar, removed lightning bolt button
  • "Solutions Library" rename from "Step Library" site-wide
  • Maintenance flows hidden from UI for pilot
  • Landing page copy rewrite: copilot-first messaging ("Resolve tickets faster. Notes write themselves.")
  • Spring bounce hover animation on dashboard cards
  • Amber "New Session" button in sidebar
  • Landing page design audit: hamburger menu, Privacy/Terms pages, branding alignment
  • Root directory cleanup: archived 9 completed docs, tracked marketing assets
  • GitHub issues triage: closed 10 stale issues (6 completed, 4 deferred)

Tech Stack

Backend

  • Framework: Python FastAPI
  • Database: PostgreSQL 16 (async via SQLAlchemy 2.0 + asyncpg)
  • Migrations: Alembic
  • Auth: JWT (python-jose) + bcrypt, refresh token rotation (JTI-based)
  • Validation: Pydantic v2
  • Scheduling: APScheduler 3.x (async, in-process with FastAPI lifespan) + croniter + pytz

Frontend

  • Framework: React 19 + Vite + TypeScript
  • Styling: Tailwind CSS v4 (@tailwindcss/vite plugin, CSS-only config in index.css) — flat dark theme with cyan accent (see DESIGN-SYSTEM.md)
  • State: Zustand (with immer + zundo for undo/redo)
  • Routing: React Router v7
  • API Client: Axios with token refresh interceptor
  • Icons: Lucide React

Key Project Structure

patherly/
├── backend/
│   ├── app/
│   │   ├── main.py                 # FastAPI entry point
│   │   ├── api/endpoints/          # Route handlers (auth, trees, sessions, admin, steps, survey, copilot, assistant_chat, psa_connections)
│   │   │   ├── flow_proposals.py   # Knowledge Flywheel review queue CRUD
│   │   │   └── flowpilot_analytics.py # FlowPilot dashboard metrics
│   │   ├── api/deps.py             # Auth dependencies (includes require_team_admin)
│   │   ├── api/router.py           # Route registration
│   │   ├── core/                   # config, database, permissions, security, audit, rate_limit
│   │   ├── models/                 # SQLAlchemy models (includes FlowProposal)
│   │   ├── schemas/                # Pydantic schemas
│   │   ├── services/psa/           # PSA provider abstraction (base, connectwise/, cache, encryption, registry, types)
│   │   ├── services/knowledge_flywheel.py        # AI session analysis → flow proposals
│   │   ├── services/knowledge_flywheel_scheduler.py # APScheduler job for batch analysis
│   │   └── services/knowledge_gap_service.py     # Weak options & escalation signal detection
│   ├── alembic/                    # Database migrations (001-029+)
│   ├── scripts/                    # seed_data.py, seed_trees.py
│   └── tests/                      # pytest integration tests
├── frontend/
│   ├── src/
│   │   ├── api/                    # Axios client + endpoint modules
│   │   ├── components/             # common, layout, dashboard, tree-editor, session, procedural, procedural-editor, library, step-library, ui, flowpilot
│   │   ├── hooks/                  # usePermissions, useSessionTimer, useKeyboardShortcuts
│   │   ├── pages/                  # All page components
│   │   ├── store/                  # Zustand stores (auth, treeEditor, proceduralEditor, userPreferences)
│   │   └── types/                  # TypeScript interfaces
│   └── (Tailwind v4: CSS-only config in src/index.css)
├── docs/plans/archive/             # Archived design/impl docs (pre-March 2026)
├── CLAUDE.md                       # This file
├── CURRENT-STATE.md                # Detailed feature status
├── LESSONS-LEARNED.md              # (Deprecated — consolidated into CLAUDE.md)
└── docs/plans/                     # Design docs & implementation plans

Environment Variables

Backend (backend/.env)

APP_NAME=ResolutionFlow
DEBUG=true
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/patherly
DATABASE_URL_SYNC=postgresql://postgres:postgres@localhost:5432/patherly
SECRET_KEY=<openssl rand -hex 32>
ACCESS_TOKEN_EXPIRE_MINUTES=5
REFRESH_TOKEN_EXPIRE_DAYS=7
REQUIRE_INVITE_CODE=true

Frontend (frontend/.env.local - optional)

VITE_API_URL=http://localhost:8000

ConnectWise PSA Integration

ResolutionFlow integrates with ConnectWise PSA (formerly Manage) as the primary PSA integration. All ConnectWise API reference materials live in docs/connectwise/.

Best Practices Documentation

Official ConnectWise developer guides live in docs/connectwise/best-practices/. Read these BEFORE implementing any CW API integration code:

  • PSA-API-Requests.md — HTTP methods, response codes, condition query syntax, PATCH format, URL encoding, partial responses, custom fields. READ FIRST.
  • PSA-Callbacks.md — Callback type/level matrix, retry behavior, URL parameter gotcha, HMAC signature verification.
  • PSA-Pagination.md — Navigable vs Forward-Only pagination, Link headers, while-loop pattern.
  • PSA-Service-Tickets.md — Ticket field philosophy, recommended field mappings.
  • PSA-Versioning.md — Pin API version via Accept header. Use application/vnd.connectwise.com+json; version=2025.16.
  • PSA-Cloud-URL-Formatting.md — Dynamic base URL construction via /login/companyinfo/{companyId}.
  • Bundled-Requests.md — Batch multiple API calls into one request via /system/bundles.
  • PSA-Markdown.md — Ticket notes support markdown. Format session documentation output accordingly.
  • PSA-Company-Synchronization.md — Filter companies by Status/Type for mapping UI.
  • PSA-Data-Protection.md — Security role model, request minimal permissions (MY not ALL).

Reference Files (read in this order)

  1. docs/connectwise/CONNECTWISE-API-REFERENCE.md — Read FIRST. Quick reference covering auth patterns, tiered endpoint map, key field mappings, and integration architecture flows.
  2. docs/connectwise/connectwise-psa-resolutionflow-reference.json — Extracted OpenAPI 3.0.1 spec (v2025.16) with only the 670 endpoints and 342 schemas relevant to ResolutionFlow. Use for exact field types, request/response shapes, and parameter details.
  3. docs/connectwise/connectwise-psa-openapi-full.json — Complete ConnectWise PSA OpenAPI spec (1838 endpoints, 842 schemas). Only consult if you need an endpoint outside the extracted subset.

Integration Architecture

  • Session → Ticket Notes: Post auto-generated session documentation to ConnectWise tickets as internal analysis notes via POST /service/tickets/{id}/notes
  • Ticket Context → Session Runner: Pull ticket details, company info, and attached configurations to give FlowPilot AI real-world context
  • Callbacks: Register webhooks via /system/callbacks for real-time ticket event notifications to suggest relevant Flows

Key Implementation Rules

  • 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 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
  • Design for the Autotask integration following the same service layer pattern (future PSA)
  • In-memory TTL cache in services/psa/cache.py for board/status/priority lookups
  • Respect CW API: paginate with max 1000 per page, handle retries gracefully

Development Commands

# Start PostgreSQL
docker start patherly_postgres

# Backend (from backend/)
source venv/bin/activate        # Linux/Mac
# .\venv\Scripts\Activate       # Windows
uvicorn app.main:app --reload

# Frontend (from frontend/)
npm run dev

# Run tests (from backend/)
pytest --override-ini="addopts="

# First time only: create test database
docker exec -it patherly_postgres psql -U postgres -c "CREATE DATABASE patherly_test;"

# Frontend build (IMPORTANT: stricter than tsc --noEmit — always use as final check)
cd frontend && npm run build

# Database migrations
cd backend && alembic upgrade head
alembic revision --autogenerate -m "Description" --rev-id=NNN  # NNN = next sequential number

# Access PostgreSQL
docker exec -it patherly_postgres psql -U postgres -d patherly

# Seed data
cd backend && pip install httpx && python -m scripts.seed_trees

# CI/CD debugging
gh run list --limit 5                          # Recent CI runs
gh run view <id> --log-failed                  # Failed job logs
gh run watch <id> --exit-status                # Watch run until complete
gh run view <id> --json jobs --jq '.jobs[] | {name: .name, conclusion: .conclusion}'

URLs

Test Users (seeded via scripts/seed_test_users.py)

  • All share password: TestPass123!
  • admin@resolutionflow.example.com (super_admin), teamadmin@resolutionflow.example.com (team_admin), engineer@resolutionflow.example.com (engineer), pro@resolutionflow.example.com (solo pro)

Critical Lessons Learned

Lessons 1-40 archived to docs/LESSONS-ARCHIVE.md — fixes are baked into the codebase. Consult if you hit a regression.

Active Lessons (41+)

41. Assistant chat uses local React state, not Zustand: AssistantChatPage.tsx uses useState for chats, messages, input, loading. No store.

42. Public pages use raw fetch(), not apiClient: Survey, shared sessions, and no-auth pages use fetch() with full URL. apiClient requires auth tokens.

43. Adding new email types: Add static async method to EmailService in core/email.py. Fire-and-forget from endpoints (log errors, don't fail).

44. AI Chat Builder is flow-type-aware: ai_chat_service.py dispatches by flow_type. Troubleshooting: [TREE_UPDATE] markers. Procedural: [STEPS_UPDATE] markers. Both support [METADATA].

45. Intake form field schema: Uses variable_name and field_type (NOT name and type).

46. CreateFlowDropdown uses AIPromptDialog: Opens prompt modal, starts AI session, generates flow, navigates to editor with { state: { aiPanelOpen: true, sessionId } }.

47. Editor-Embedded Flow Assist: EditorAIPanel (320px side panel) + useEditorAI hook. Ghost nodes use _suggestion: true flag. Actions route to model tiers via settings.get_model_for_action(). Delta responses use [DELTA]...[/DELTA] markers.

48. Tree orphan validation uses dynamic root ID: Orphan check compares against state.treeStructure?.id (NOT hardcoded 'root').

49. Full-stack features — verify both ends: Check the full data flow: schema → endpoint → API client → hook → store → UI.

50. Anthropic SDK retry: Set max_retries=1 to fail fast. Default max_retries=2 can take 3× timeout.

51. AI model tier routing: Use settings.get_model_for_action(action_type). Model IDs: use alias form (claude-sonnet-4-6).

52. Mobile scroll-to-top: Use ref.current.scrollIntoView(), not window.scrollTo(). Trigger via useEffect.

53. Flex height chain: Every ancestor must be a flex container for flex-1 to work. Missing flex class collapses React Flow to 0 height.

54. React Flow CSS in Tailwind v4: Import in index.css, not component JS. Override dark theme using --xy-* CSS custom properties.

55. App shell height chain: Every wrapper between .main-content and canvas needs flex + flex-1 + min-h-0 or h-full.

56. Railway backend service name is patherly: Production DB name is railway. Public Postgres proxy: interchange.proxy.rlwy.net:45797.

57. Node field priority: titlequestiondescriptioncontentlabel. See copilot_service.py.

58. scriptGeneratorStore.generate() optional param: Always wrap: onClick={() => generate()}, never onClick={generate}.

59. ConnectWise clientId is server-side config: Set in config.py as CW_CLIENT_ID. Per-connection: company_id, public_key, private_key, server_url.

60. Dockerfile build args for Vite env vars: Any new VITE_* or VITE_PUBLIC_* env var must be added as ARG + ENV in frontend/Dockerfile for Railway deploys. Railway env vars are runtime-only unless explicitly passed through as Docker build args. Without this, import.meta.env.VITE_* resolves to undefined in production builds.

61. Procedural sessions auto-start on page load: ProceduralNavigationPage calls startSession() immediately in loadTree() — there is no intake form screen or "Start" button. Variables are filled inline during execution. Troubleshooting flows DO have a start screen with ticket/client fields. Don't write tests or UI that assume a Start button on procedural flows.

62. Playwright strict mode — scope selectors to avoid ambiguity: Step titles appear in both the sidebar checklist and main content heading. Use getByRole('heading', { name }) for the main content, or scope with page.locator('.animate-scale-in') for command palette items. getByText() frequently matches multiple elements due to the sidebar + main content layout.

63. Node 20 required for frontend builds: Vite 7+ requires Node 20.19+. The system Node may be v18; use nvm: export NVM_DIR="$HOME/.nvm" && source "$NVM_DIR/nvm.sh" && nvm use 20. For direct binary access without nvm sourcing: PATH="/home/michaelchihlas/.nvm/versions/node/v20.19.0/bin:$PATH".

64. PostHog product analytics: Initialized via PostHogProvider in main.tsx with explicit posthog.init() + client prop pattern. Event helpers in lib/analytics.ts — use analytics.eventName(props) to track. identifyUser() called in authStore.fetchUser(), resetAnalytics() on logout. Env vars: VITE_PUBLIC_POSTHOG_KEY, VITE_PUBLIC_POSTHOG_HOST. Autocapture enabled.

65. Local Docker Compose uses resolutionflow database on port 5433: Container name is resolutionflow_postgres, database is resolutionflow (not patherly), port mapped to 5433 (not 5432). The POSTGRES_PORT env var controls this. Playwright config defaults must match: postgresql+asyncpg://postgres:postgres@127.0.0.1:5433/resolutionflow.

66. Dev environment runs on devserver01 (192.168.0.9), not localhost: Code-server runs in Docker on a LAN server. Frontend/backend are accessed via 192.168.0.9, not localhost. CORS must include http://192.168.0.9:5173 in CORS_ORIGINS and FRONTEND_URL. Frontend .env must set VITE_API_URL=http://192.168.0.9:8000. See DEV-ENV.md for full setup, Docker config, networking, and known issues.

67. Tree editor route is /trees/new: NOT /editor/new. Check router.tsx line 156 for the canonical path. Use getTreeEditorPath() from @/lib/routing when navigating programmatically.

68. APScheduler jobs need max_instances=1: Without it, overlapping scheduler runs can process the same records twice (TOCTOU race). Always set max_instances=1 on interval jobs in main.py.

69. PostgreSQL func.sum(case(...)) returns Decimal via asyncpg: Cast to int() before storing in Pydantic dict[str, Any] fields, or JSON serialization may produce unexpected types.

70. Toast library uses toast.warning() not toast.warn(): Import from @/lib/toast. Methods: success, error, warning, info. See frontend/src/lib/toast.ts.

71. Enhancement/branch_addition proposals cannot be directly approved: Backend returns 400 — they require modified_flow_data via "Edit & Publish" flow. Only new_flow proposals support direct approve.

72. ai_sessions.status column is VARCHAR(30): Must fit requesting_escalation (23 chars). If adding new status values, verify length. Migration f0aad74ea51b widened from 20→30.

73. get_db rolls back on exception: The dependency does await session.rollback() on error to prevent InFailedSQLTransaction cascade. Never remove this — without it, one failed request poisons subsequent requests on the same connection.

74. FlowPilot action bar height chain: The action bar (Resolve/Escalate/Pause) requires every ancestor from app-shell grid down to have proper flex constraints. Key fix: ViewTransitionOutlet wrapper needs flex flex-col. If action bar disappears, check height chain with DevTools getBoundingClientRect() walk.

75. Dashboard prefill auto-submits: StartSessionInput navigates to /pilot or /assistant with { state: { prefill } }. FlowPilotSessionPage auto-submits via useEffect + prefillHandledRef guard — no double-enter. AssistantChatPage does the same pattern.

76. Active session navigation guard: FlowPilotSessionPage uses useBlocker (same as TreeEditorPage) to intercept navigation during active sessions. "Pause & Leave" auto-pauses before proceeding.

77. Prefer manual Alembic migrations for targeted changes: alembic revision --autogenerate picks up drift from all tables. For single-column fixes, use alembic revision -m "desc" and write op.alter_column() manually.

78. Landing page subtitle is "AI-Powered Troubleshooting for MSPs": Not "Decision Tree Platform". This tagline appears on login, register, and the HTML <title>. The old "Decision Tree Platform" was internal jargon misaligned with user-facing branding.

79. Custom modals must be mobile-responsive: Use items-end sm:items-center (bottom-sheet on mobile, centered on desktop) and max-w-full sm:max-w-lg (full-width on mobile). The shared Modal.tsx does this correctly — custom modal implementations must follow the same pattern. See PrepareSessionModal.tsx for the fix pattern.

80. TopBar search collapses to icon on mobile: Full search bar (hidden sm:block) shows on desktop; magnifying glass icon button (sm:hidden) shows on mobile (<640px). Both open the same CommandPalette. Don't add w-full search bar without the mobile icon fallback.

81. Never use transition: all in landing.css: Specify exact properties: transition: background 0.3s, border-color 0.3s, box-shadow 0.3s, transform 0.3s, opacity 0.3s. transition: all animates layout properties and causes jank.

82. bun requires PATH setup on devserver01: export BUN_INSTALL="$HOME/.bun" && export PATH="$BUN_INSTALL/bin:$PATH". The gstack browse binary and Playwright need this. Chromium system deps: libatk1.0-0 libatk-bridge2.0-0 libcups2 libxkbcommon0 libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2.

83. FlowPilot ActionBar is position: fixed; bottom: 0: Any UI element placed in normal document flow below the session content will be hidden behind it. New fixed-position elements (like the message bar) must use bottom: 68px (action bar height) and the same left: var(--sidebar-w) pattern. The conversation column uses pb-32 for clearance.

84. AI session abandoned status is fully wired: POST /ai-sessions/{id}/abandon sets status to abandoned with optional reason param. Frontend: aiSessionsApi.abandonSession(), useFlowPilotSession().abandonSession(), "Close" button in FlowPilotActionBar. Redirects to /sessions after closing.

85. Date range filter end dates must use end-of-day: toDate.toISOString() sends midnight (start of day), excluding items created later that day. Always set toDate.setHours(23, 59, 59, 999) before sending. For string-based date inputs (AI sessions), append T23:59:59.999Z. See SessionHistoryPage.tsx.

86. Script Builder system: AI-powered script generation at /script-builder. Chat-style interface generates PowerShell/Bash/Python scripts from natural language. Backend: ScriptBuilderSession model, script_builder_service.py, endpoints at /scripts/builder/. Frontend: ScriptBuilderPage, ScriptCodeBlock, ScriptPreviewModal, SaveToLibraryDialog. FlowPilot can hand off to Script Builder via action_type: "open_script_builder" with sessionStorage context passing.

87. FlowPilot must ask GUI vs script preference: When a task can be done via GUI or script (e.g., creating AD users), FlowPilot must ask the engineer which approach they prefer BEFORE suggesting either. Never assume the user wants a script. See FLOWPILOT_SYSTEM_PROMPT rules in flowpilot_engine.py.

88. Charcoal palette — sidebar-darkest approach: Sidebar #10121a, page #1a1c23, cards #22252e, borders #2e3240. This gives more contrast range than true-dark (#0c0d10). All colors via CSS variables in index.css @theme block. Accent is ember orange (#f97316), not cyan.

89. QuickLaunch merged into CommandPalette: There is no separate QuickLaunch/lightning bolt. The unified Cmd+K omnibar handles search, navigation, quick actions, and FlowPilot. QuickLaunch.tsx was deleted.

90. Copilot-first UX direction: The FlowPilot AI chat copilot is the primary experience. Dashboard centers on the chat input. Guided flows (decision trees) are accessible but secondary — in sidebar under "Flows". Maintenance flows are hidden from UI for pilot.

91. "New Session" button is amber-400: Sidebar uses bg-amber-400/15 text-amber-400 for the New Session button, not cyan. This makes it visually distinct from the cyan accent used elsewhere.

92. tsc -b in Dockerfile is stricter than npx tsc --noEmit: The production build (tsc -b && vite build) enforces noUnusedLocals and noUnusedParameters as hard errors. After any refactor that moves logic between components or removes features, trace every import and destructured prop to remove orphans. IDE warnings (yellow squiggles) flag these — check them before pushing.

93. FlowPilot actions live in the page header, not a bottom bar: FlowPilotSessionPage renders Resolve/Escalate/Share Update in the header bar. Desktop: inline buttons + overflow (Pause/Close). Mobile: single menu. The bottom only has the message input. FlowPilotActionBar component still exists but is no longer used in the main session flow.

94. Frontend chat uses unified_chat_service, not assistant_chat_service: AssistantChatPage calls /ai-sessions/{id}/chatunified_chat_service.py. The old assistant_chat_service endpoints were removed (only retention settings remain at /assistant/retention). When tracing chat features, start from aiSessionsApi.sendChatMessageai_sessions.pyunified_chat_service.py. Never wire chat features into assistant_chat.py.

95. Image upload → AI vision pipeline: Paste/attach images → upload to Railway S3 bucket via uploadsApi.upload() → send upload_ids with chat message → backend fetches from S3 via storage_service.download_file() → resized via storage_service.resize_image_for_vision() (Pillow, 1568px max, PNG→JPEG) → base64-encoded → sent as Claude multimodal content blocks. Max 3 images/message. Images are NOT stored in conversation history (text-only). Vision helpers live in storage_service.py.

96. bg-accent is ember orange — never use for code/kbd elements: In Tailwind v4, bg-accent maps to --color-accent: #f97316. Use bg-code for code blocks, bg-white/[0.12] border border-white/[0.06] for inline code/badges, bg-white/[0.08] for kbd shortcuts. Orange is reserved for interactive elements only (buttons, active nav, links).

97. Railway Object Storage (S3 bucket) is provisioned: Bucket resolutionflow-uploads on Railway canvas. Variables: STORAGE_ENDPOINT, STORAGE_ACCESS_KEY, STORAGE_SECRET_KEY, STORAGE_BUCKET_NAME, STORAGE_REGION — mapped via variable references on the patherly backend service. Accessed via boto3 in storage_service.py. Pillow (Pillow>=10.0.0) + libjpeg-dev/zlib1g-dev in Dockerfile for image resize.

98. lazyWithRetry for stale chunk errors: All lazy-loaded routes use lazyWithRetry from @/lib/lazyWithRetry.ts instead of React.lazy. Auto-reloads the page on chunk load failures (stale deploys). Uses sessionStorage debounce (10s) to prevent loops. When adding new lazy routes, use lazyWithRetry, not lazy.


RBAC & Permissions

  • Role hierarchy: super_admin > team_admin > engineer > viewer
  • Team Admin: role='engineer' + is_team_admin=True + valid team_id
  • Backend deps: get_current_active_user(user, db) (any active + auto-downgrades expired trials), require_engineer_or_admin (blocks viewers), require_admin (super admin only)
  • Never use role == "admin" — use is_super_admin instead
  • Frontend: usePermissions() hook for all permission checks
  • Centralized: backend/app/core/permissions.py, frontend/src/hooks/usePermissions.ts

Design System

Source of truth: DESIGN-SYSTEM.md — always read this before making visual or UI decisions.

  • Theme: Flat, high-contrast dark theme (Sentry/PostHog-inspired). No glass morphism, no backdrop blur, no ambient orbs, no gradient backgrounds on surfaces. Light mode planned.
  • Backgrounds: bg-page (#1a1c23), bg-sidebar (#10121a), bg-card (#22252e), bg-elevated (#2e3140)
  • Cards: bg-card with 1px border-default (#2e3240), 8px radius. No shadows, no blur, no gradients. Hover: border-hover (#3d4252)
  • Buttons: Primary: solid accent (#f97316), white text, 5px radius. Ghost: transparent + 1px border, hover bg-elevated
  • Inputs: bg-input (#282b35) with 1px border-default, 5px radius. Focus: border-color: accent + box-shadow: 0 0 0 2px accent-dim
  • Text: text-heading (#f0f2f5) → text-primary (#e2e5eb) → text-secondary (#848b9b) → text-muted (#4f5666)
  • Borders: border-default (#2e3240), border-hover (#3d4252)
  • Functional colors: #34d399 (success), #eab308 (warning), #f87171 (danger) — each with -dim variant at 10% opacity
  • Accent: Ember orange #f97316 — used sparingly (≤5% of UI). accent-dim = rgba(249,115,22,0.10), accent-text = #fdba74
  • Deprecated: Do NOT use glass-card, glass-stat, bg-gradient-brand, text-gradient-brand, backdrop-filter: blur(), ambient orbs, purple gradients, or cyan accent (#22d3ee)

Frontend Patterns

  • Component guidelines: Use cn() from @/lib/utils, Lucide icons (wrap in <span> for title), modals with fixed header/footer
  • Type organization: Create in types/, export from types/index.ts, import with import type { T } from '@/types'
  • Scratchpad overlay: position: fixed, onOpenChange callback for parent padding adjustment, right-2 positioning
  • Custom step flow: CustomStepModalPostStepActionModalContinuationModal → custom step view. Key state: pendingStep, pendingContinuationNodeId, customBranchMode, branchOriginNodeId. Use findCustomStep() not findNode() for custom step UUIDs.
  • Session sharing: ShareSessionModal manages share links, SharedSessionPage renders public/account views. Helper utils in lib/sessionShare.ts. Share URLs use /shared/sessions/:token.
  • Procedural navigation: ProceduralNavigationPage handles intake forms, step-by-step execution, and resume via location.state.sessionId. Uses StepChecklist, StepDetail, ProgressBar, CompletionSummary components.
  • Routing helper: Use getTreeNavigatePath() and getTreeEditorPath() from @/lib/routing for all tree/session navigation.
  • Account section layout: AccountLayout has NO sidebar nav. Account sub-pages (categories, target-lists) are reached via link cards on AccountSettingsPage.tsx. New account pages: add route in router.tsx under account children + add a link card in AccountSettingsPage.
  • Dashboard cockpit: QuickStartPage is the copilot-first launchpad. Greeting + "What are you troubleshooting?" + ChatGPT-style StartSessionInput (auto-growing textarea, paste images, drag-drop files, attach button, paste logs, suggestion chips). Below: PendingEscalations, ActiveFlowPilotSessions, RecentFlowPilotSessions. Collapsible "Dashboard" section for PerformanceCards, KnowledgeBaseCards, TeamSummary.
  • Sidebar sections: Amber "New Session" button → Home → RESOLVE (History) → KNOWLEDGE (Flows with Solutions Library sub-item, Scripts) → INSIGHTS (Data). Footer: Account, Pin/Unpin. No help/guides/feedback in sidebar — accessible via TopBar.

Common Tasks

  • New endpoint: Create in endpoints/ → add to router.py → schema in schemas/ → tests → frontend API client
  • New page: Create in pages/ → add route in router.tsx → nav link in AppLayout.tsx
  • New public route (no auth): Add at top level in router.tsx alongside /login, /register — NOT inside the ProtectedRoute/AppLayout children.
  • Schema change: Update model → alembic revision --autogenerate -m "desc" → review → alembic upgrade head
  • New frontend API module: Types in types/ → export from types/index.ts → client in api/ → export from api/index.ts

Coding Standards

Python

  • Type hints everywhere, async/await for DB, Pydantic for validation, DateTime(timezone=True) always

TypeScript

  • Interfaces for all data, const over let, functional components + hooks, reusable logic in custom hooks

Git

  • Format: type: description (feat, fix, refactor, docs, test, chore)
  • Always include Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
  • Always create feature branch BEFORE committing: git checkout -b feat/feature-name
  • Large features: commit per phase with npm run build validation

After Completing Work

When a feature, fix, or significant piece of work is finished and merged/committed:

  1. Update CURRENT-STATE.md — move completed items, update "In Progress" and "What's Next" sections
  2. Update 03-DEVELOPMENT-ROADMAP.md — check off completed work, update phase status
  3. Close related GitHub Issues — use gh issue close #N for any issues resolved by the work
  4. Update CLAUDE.md if the work introduced new patterns, lessons learned, or changed project structure

gstack (Browser & Workflow Skills)

Web browsing: Always use the /browse skill from gstack for all web browsing needs. Never use mcp__claude-in-chrome__* tools.

Available skills:

Skill Purpose
/office-hours Brainstorm new ideas (YC-style office hours)
/plan-ceo-review CEO/founder-mode plan review (scope, ambition)
/plan-eng-review Engineering plan review (architecture, edge cases)
/plan-design-review Design plan review (UI/UX critique)
/design-consultation Create a design system / DESIGN.md
/review Pre-landing PR code review
/ship Ship workflow (tests, review, PR creation)
/browse Headless browser for QA testing and site dogfooding
/qa Systematic QA testing + auto-fix bugs found
/qa-only QA report only (no fixes)
/design-review Visual QA — find and fix design inconsistencies
/setup-browser-cookies Import cookies from real browser for authenticated testing
/retro Weekly engineering retrospective
/investigate Systematic debugging with root cause analysis
/document-release Post-ship documentation updates
/codex Second opinion via OpenAI Codex CLI
/careful Safety guardrails for destructive commands
/freeze Restrict edits to a specific directory
/guard Full safety mode (careful + freeze)
/unfreeze Remove edit restrictions
/gstack-upgrade Upgrade gstack to latest version

Deployment (Railway)

  • Production: resolutionflow.com (frontend), api.resolutionflow.com (backend)
  • Auto-deploys on push to main
  • PR environments auto-created (need manual domain generation in Railway dashboard)
  • PR envs need VITE_API_URL set with https:// prefix on frontend service
  • ALLOW_RAILWAY_ORIGINS=true enables CORS for *.up.railway.app
  • Shared Variables (project-level in Railway dashboard) auto-propagate to all environments including PR envs — use for secrets like ANTHROPIC_API_KEY
  • Super admin utility: backend/make_superadmin_simple.py list|<email>

gstack

Use /browse from gstack for all web browsing — never use mcp__claude-in-chrome__* tools.

Available skills: /office-hours, /plan-ceo-review, /plan-eng-review, /plan-design-review, /design-consultation, /review, /ship, /browse, /qa, /qa-only, /design-review, /setup-browser-cookies, /retro, /investigate, /document-release, /codex, /careful, /freeze, /guard, /unfreeze, /gstack-upgrade


Future Roadmap

  • Phase 3: PSA integrations (ConnectWise in progress), file attachments, client context, analytics
  • Phase 4: Additional PSA integrations (Autotask/Kaseya), PowerShell automation, enterprise SSO

Quick Reference

What Where
API Docs http://localhost:8000/api/docs
Detailed Status CURRENT-STATE.md
Development Roadmap 03-DEVELOPMENT-ROADMAP.md
GitHub Issues gh issue list --state open
Bugs & Fixes CLAUDE.md → Critical Lessons Learned section
Feature Specs 04-FEATURE-SPECIFICATIONS.md
Design System DESIGN-SYSTEM.md
Dev Environment DEV-ENV.md — 46.202.92.250 setup, Docker, CORS, networking