16 KiB
CLAUDE.md - Patherly / ResolutionFlow Project Context
Last Updated: February 14, 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: Dark-first with purple gradient accents (
#818cf8→#a78bfa). NOT monochrome — use gradient for primary buttons, active nav indicators, stat highlights, and brand text. - Fonts: Plus Jakarta Sans (
font-heading, headings/titles), Inter (font-sans, body text), Outfit (font-label, labels/badges/counts) — loaded via Google Fonts - Logo: Inline SVG in
BrandLogo.tsx(decision-tree icon with gradient). Wordmark: "Resolution" in white + "Flow" intext-gradient-brand - Brand assets:
brand-assets/(source SVGs + brand-guide.html),frontend/src/assets/brand/(app assets),frontend/public/icons/(favicon) - CSS utilities:
text-gradient-brand,bg-gradient-brand,bg-gradient-brand-hover(defined intailwind.config.jsandindex.css) - Layout: App shell with persistent sidebar + top bar + main workspace (CSS Grid). See UI-DESIGN-SYSTEM.md
- Workspace system: Top-level context switcher (Troubleshooting, Procedures, Policies, Finance). Sidebar categories, tags, stats, and content adapt per workspace. See UI-DESIGN-SYSTEM.md
- Rebrand guide: REBRAND-IMPLEMENTATION-GUIDE.md
- Interactive mockup:
docs/mockups/resolutionflow-workspaces-mockup.html(open in browser for visual reference)
Component styling rules:
- Primary buttons:
bg-gradient-brandwithshadow-lg shadow-primary/20, hover lifts with stronger shadow - Secondary buttons:
bg-cardwithborder-border, hover brightens border - Active nav items:
bg-primary/8background + 3px left gradient accent bar - Stat values: use
text-gradient-brandfor highlighted metrics - Status colors: green (
text-green-500) for success, amber (text-amber-500) for in-progress, red (text-red-500) for error/critical - Category dots: 8px colored circles using the category color palette
- Tags/badges:
font-label(Outfit), small rounded chips withbg-card border-border - Cards:
bg-card border-border rounded-xl, hover brightens border - Section labels:
font-label text-[0.6875rem] uppercase tracking-wide text-muted-foreground
When adding new pages/components: use "ResolutionFlow" branding, purple gradient accent theme, bg-card containers, text-foreground/text-muted-foreground hierarchy. Primary actions use bg-gradient-brand. Pages render inside the app shell (CSS Grid: topbar + sidebar + main). Reference UI-DESIGN-SYSTEM.md for layout patterns, workspace context, and component specs.
Current State
- Phase: Phase 2.5 - Step Library Foundation (In Progress)
- Backend: Complete (25+ API endpoints, 100+ integration tests)
- Frontend: Core features complete, Tree Editor functional
- Database: PostgreSQL with Docker, 30+ migrations
- Detailed status: CURRENT-STATE.md
What's In Progress
- Procedural flows reusability and run lifecycle improvements (Phase 2)
- Step Library Frontend UI
Recently Completed
- Session sharing: ShareSessionModal, SharedSessionPage, MySharesPage, share links with copy/manage
- Procedural editor UX: section headers as step type, "More Options" collapsible, URL intake field, tag input improvements
- Type-aware routing: centralized
getTreeNavigatePathhelper, procedural resume support, safety redirects - Export improvements (Phases A-C): step cutoff, summary block, detail levels, editable preview, sensitive data redaction
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
Frontend
- Framework: React 19 + Vite + TypeScript
- Styling: Tailwind CSS v3 — dark-first with purple gradient accents (see Branding section)
- 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, etc.)
│ │ ├── api/deps.py # Auth dependencies
│ │ ├── api/router.py # Route registration
│ │ ├── core/ # config, database, permissions, security, audit, rate_limit
│ │ ├── models/ # SQLAlchemy models
│ │ └── schemas/ # Pydantic schemas
│ ├── 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, tree-editor, session, procedural, procedural-editor, library, step-library, ui
│ │ ├── hooks/ # usePermissions, useSessionTimer, useKeyboardShortcuts
│ │ ├── pages/ # All page components
│ │ ├── store/ # Zustand stores (auth, treeEditor, proceduralEditor, userPreferences)
│ │ └── types/ # TypeScript interfaces
│ └── tailwind.config.js
├── CLAUDE.md # This file
├── CURRENT-STATE.md # Detailed feature status
├── LESSONS-LEARNED.md # Bugs and fixes (READ THIS!)
└── 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
Development Commands
# Start PostgreSQL
docker start patherly_postgres
# Backend (from backend/)
.\venv\Scripts\Activate
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"
# Access PostgreSQL
docker exec -it patherly_postgres psql -U postgres -d patherly
# Seed data
cd backend && pip install httpx && python -m scripts.seed_trees
URLs
- Frontend: http://localhost:5173
- Backend API: http://localhost:8000
- API Docs: http://localhost:8000/api/docs
Critical Lessons Learned
Full reference: LESSONS-LEARNED.md — read before making changes!
Top Gotchas (most commonly hit)
1. DateTime Handling — Always timezone-aware:
# CORRECT
created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
# NEVER use datetime.utcnow()
2. SQLAlchemy Async — No lazy loading on new objects:
# WRONG — MissingGreenlet error
new_tree = Tree(...); db.add(new_tree); await db.flush()
new_tree.tags.append(tag) # Lazy load fails!
# CORRECT — Use direct SQL for junction tables
await db.execute(tree_tag_assignments.insert().values(tree_id=new_tree.id, tag_id=tag.id))
3. React State — Don't store object snapshots:
// WRONG — snapshot won't update
const [editingNode, setEditingNode] = useState(node)
// CORRECT — store ID, derive object
const [editingNodeId, setEditingNodeId] = useState(node.id)
const editingNode = editingNodeId ? findNode(editingNodeId, tree?.tree_structure) : null
4. Modal Draft State — Exclude store-managed fields:
const { children, ...draftWithoutChildren } = draft
updateNode(node.id, draftWithoutChildren) // Don't overwrite children
5. Multiple FKs to same table — Specify foreign_keys on BOTH sides:
author = relationship("User", foreign_keys=[author_id], back_populates="trees")
6. PostgreSQL NULL in UUID columns:
SELECT 'tag', 'slug', NULL::uuid as team_id -- Must cast NULL to uuid
7. API Path Gotcha: Frontend apiClient baseURL is http://localhost:8000/api/v1 — use relative paths WITHOUT /api/v1/ prefix (e.g., /admin/users not /api/v1/admin/users). Invite codes endpoint is /invites (NOT /invite-codes).
8. CORS errors can mask 500s: Check backend logs first. Also run alembic upgrade head after pulling changes.
11. CORS expose_headers for custom response headers: Browsers block frontend from reading custom headers (e.g. X-Redaction-Summary) unless CORS middleware includes expose_headers=["X-Custom-Header"]. Must be set in BOTH CORS branches in main.py.
9. Public endpoints with optional auth: Use manual _get_optional_user(request, db) helper, NOT Optional[User] param (FastAPI treats it as Pydantic field).
10. React Router — Clear dirty state before navigation:
markSaved() // Clear isDirty BEFORE navigate()
navigate(`/trees/${newTree.id}/edit`)
12. TreeStructure vs Tree types: TreeStructure is for node structure only — it does NOT have tree_type, name, etc. Those are on Tree. JSONB tree snapshots need TreeStructure & Record<string, unknown> for extra fields.
13. Login redirect state format: navigate('/login', { state: { from: { pathname: '/path' } } }) — LoginPage expects state.from.pathname (object), NOT a plain string.
14. Type-aware routing for trees/sessions: Always use getTreeNavigatePath(treeId, treeType) from @/lib/routing instead of hardcoding /trees/:id/navigate. Procedural flows use /flows/:id/navigate. Session resume passes { state: { sessionId } }. TreeNavigationPage has a safety redirect for procedural trees.
15. Session sharing types: TreeSnapshot extends TreeStructure but session snapshots from the API include extra fields like tree_type. Use tree_snapshot?.tree_type to determine flow type from session data.
RBAC & Permissions
- Role hierarchy: super_admin > team_admin > engineer > viewer
- Team Admin:
role='engineer'+is_team_admin=True+ validteam_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"— useis_super_admininstead - Frontend:
usePermissions()hook for all permission checks - Centralized:
backend/app/core/permissions.py,frontend/src/hooks/usePermissions.ts
Monochrome Design System
- Backgrounds:
bg-black, subtle radial gradients - Cards:
glass-card rounded-2xl(NOTbg-card border-border) - Buttons: Primary:
bg-white text-black hover:bg-white/90. Secondary:border border-white/10 text-white/60 hover:bg-white/10 - Inputs:
border-white/10 bg-black/50 text-white+ focus:border-white/30 ring-white/20 - Text:
text-white→text-white/70→text-white/40→text-white/30 - Borders:
border-white/[0.06]orborder-white/10 - Functional color only: emerald-400 (success), red-400 (error), yellow-400 (warning), blue-400 (info)
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 fromtypes/index.ts, import withimport type { T } from '@/types' - Scratchpad overlay:
position: fixed,onOpenChangecallback for parent padding adjustment,right-2positioning - Custom step flow:
CustomStepModal→PostStepActionModal→ContinuationModal→ custom step view. Key state:pendingStep,pendingContinuationNodeId,customBranchMode,branchOriginNodeId. UsefindCustomStep()notfindNode()for custom step UUIDs. - Session sharing:
ShareSessionModalmanages share links,SharedSessionPagerenders public/account views. Helper utils inlib/sessionShare.ts. Share URLs use/shared/sessions/:token. - Procedural navigation:
ProceduralNavigationPagehandles intake forms, step-by-step execution, and resume vialocation.state.sessionId. UsesStepChecklist,StepDetail,ProgressBar,CompletionSummarycomponents. - Routing helper: Use
getTreeNavigatePath()andgetTreeEditorPath()from@/lib/routingfor all tree/session navigation.
Common Tasks
- New endpoint: Create in
endpoints/→ add torouter.py→ schema inschemas/→ tests → frontend API client - New page: Create in
pages/→ add route inrouter.tsx→ nav link inAppLayout.tsx - New public route (no auth): Add at top level in
router.tsxalongside/login,/register— NOT inside theProtectedRoute/AppLayoutchildren. - Schema change: Update model →
alembic revision --autogenerate -m "desc"→ review →alembic upgrade head - New frontend API module: Types in
types/→ export fromtypes/index.ts→ client inapi/→ export fromapi/index.ts
Coding Standards
Python
- Type hints everywhere, async/await for DB, Pydantic for validation,
DateTime(timezone=True)always
TypeScript
- Interfaces for all data,
constoverlet, 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 buildvalidation
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_URLset withhttps://prefix on frontend service ALLOW_RAILWAY_ORIGINS=trueenables CORS for*.up.railway.app- Super admin utility:
backend/make_superadmin_simple.py list|<email>
Future Roadmap
- Phase 3: File attachments, offline mode, client context, analytics
- Phase 4: PSA integrations (ConnectWise, Kaseya), PowerShell automation, enterprise SSO
Quick Reference
| What | Where |
|---|---|
| API Docs | http://localhost:8000/api/docs |
| Detailed Status | CURRENT-STATE.md |
| Bugs & Fixes | LESSONS-LEARNED.md |
| Feature Specs | 04-FEATURE-SPECIFICATIONS.md |
| Design System | docs/plans/Frontend/DESIGN_SYSTEM_GUIDE.md |