32 KiB
CLAUDE.md - Patherly / ResolutionFlow Project Context
Purpose: This file provides Claude Code with essential context for working on the Patherly project. Last Updated: February 4, 2026
Project Overview
Patherly (user-facing brand: ResolutionFlow) is a troubleshooting decision tree application designed for MSP engineers. It guides engineers through proven troubleshooting paths, captures decisions and notes automatically, and generates professional ticket documentation.
Tagline: "Take the path MOST traveled."
Primary User: Michael Chihlas - Senior Systems Engineer at an MSP
Goal: Michael uses this tool for 50% of his tickets within 3 months.
Branding
The project was rebranded from "Patherly" to "ResolutionFlow" in the frontend (PR #26, commit cfbd815). The naming is split:
| Context | Name Used |
|---|---|
| Repository / directory name | patherly |
| Backend (FastAPI, env vars, APP_NAME) | ResolutionFlow |
| Database / Docker container | patherly / patherly_postgres |
| Production URLs | resolutionflow.com / api.resolutionflow.com |
| Frontend UI (header, login, register) | ResolutionFlow |
| Browser tab title | ResolutionFlow - Decision Tree Platform |
Brand details:
- Colors: Purple gradient (
#818cf8→#a78bfa) - Fonts: Plus Jakarta Sans (headings), Inter (body), Outfit (labels) - loaded via Google Fonts
- Logo: Inline SVG in
AppLayout.tsxandLoginPage.tsx(decision-tree icon with gradient) - Brand assets:
brand-assets/(source SVGs),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) - Rebrand guide: REBRAND-IMPLEMENTATION-GUIDE.md
When adding new frontend pages or components, use "ResolutionFlow" for any user-visible branding. Use font-heading for heading elements and the text-gradient-brand utility for brand-colored text.
Current State
- Phase: Phase 2.5 - Step Library Foundation (In Progress)
- Backend: Complete (20+ API endpoints, 40+ integration tests, all passing)
- Frontend: Core features complete, Tree Editor functional, Settings page added
- Database: PostgreSQL with Docker (container name:
patherly_postgres)
What's Complete
- User authentication (JWT, register, login, refresh, invite codes)
- Trees CRUD with full-text search
- Sessions tracking with decisions
- Export API (Markdown, Text, HTML)
- Tree Editor with form-based editing and visual preview
- Dark/Light theme toggle
- Markdown rendering in session player and node editor
- 7 comprehensive seed decision trees
- Tree Organization System:
- Categories (global + team-specific, admin-managed)
- Tags (author + admin managed, autocomplete)
- User folders (personal tree collections)
- Subfolder hierarchy (max 3 levels deep)
- Right-click context menu for edit/delete/add subfolder
- Cascade delete for subfolders
- Team admin role with scoped permissions
- Filter trees by category, tags, and folders
- User Preferences (Issue #3):
- Settings page at
/settings - Default export format preference (persisted in localStorage)
- Theme toggle integrated in Settings
- Settings page at
- Step Categories (Issue #5):
- Database table with 10 seeded global categories
- Full CRUD API at
/api/v1/step-categories - Team scoping support (global + team-specific)
- Step Library Schema (Issue #6):
step_librarytable for reusable troubleshooting stepsstep_ratingstable for user ratings/reviewsstep_usage_logtable for tracking verified use- Support for decision/action/solution step types
- Visibility levels: private, team, public
- Step Library API (Issue #7):
- Full CRUD at
/api/v1/steps - Full-text search endpoint
- Popular tags endpoint
- Rating/review system with verified use tracking
- Full CRUD at
- Frontend Rebrand (PR #26):
- Renamed from "Patherly" to "ResolutionFlow" in all user-facing UI
- Purple gradient theme, custom fonts (Plus Jakarta Sans, Inter, Outfit)
- Custom SVG logo in header and auth pages
- Updated favicon and browser tab title
- Token Refresh Fix:
- Silent refresh with single-flight queue (prevents concurrent 401 race conditions)
- Backend
get_refresh_token_payloaddependency extracts refresh token from Authorization header - Frontend Axios interceptor queues failed requests during refresh, retries after success
- Auth store synced after silent refresh via
setTokensaction
- Session Scratchpad (Floating Overlay):
- Fixed-position overlay panel (420px wide, 55vh tall) on right edge
- Floating button when collapsed, slide-in panel when expanded
- Ctrl+/ keyboard shortcut to toggle
- Auto-save with 1s debounce, markdown preview, localStorage persistence
- Main content adjusts width via padding transition when panel opens
- Global Thin Scrollbar Styling:
- 6px thin scrollbars site-wide (Firefox
scrollbar-width: thin+ WebKit pseudo-elements) - Theme-aware colors using CSS variables (
--border,--muted-foreground)
- 6px thin scrollbars site-wide (Firefox
What's In Progress
- Custom step continuation flow refinements (Phase 2.5)
- Tree forking from sessions with custom steps
Deployment
- Production: Railway (app.patherly.com / api.patherly.com)
- PR Environments: Enabled - auto-created for each pull request
Tech Stack
Backend
- Framework: Python FastAPI
- Database: PostgreSQL 16 (async via SQLAlchemy 2.0 + asyncpg)
- Migrations: Alembic
- Auth: JWT tokens (python-jose) + bcrypt passwords
- Validation: Pydantic v2
Frontend
- Framework: React 19 + Vite + TypeScript
- Styling: Tailwind CSS v3 with ResolutionFlow brand theme (purple gradient)
- Fonts: Plus Jakarta Sans (headings), Inter (body), Outfit (labels) via Google Fonts
- State: Zustand (with immer + zundo for undo/redo)
- Routing: React Router v7
- API Client: Axios with token interceptors
- Icons: Lucide React
Project Structure
patherly/
├── backend/
│ ├── app/
│ │ ├── main.py # FastAPI entry point
│ │ ├── api/
│ │ │ ├── endpoints/
│ │ │ │ ├── auth.py # Auth: register, login, refresh, logout
│ │ │ │ ├── trees.py # Trees CRUD + search
│ │ │ │ ├── sessions.py # Sessions + export
│ │ │ │ └── invite.py # Invite code management
│ │ │ ├── deps.py # Auth dependencies
│ │ │ └── router.py
│ │ ├── core/
│ │ │ ├── config.py # Settings (pydantic-settings)
│ │ │ ├── database.py # Async SQLAlchemy
│ │ │ ├── security.py # JWT + password hashing
│ │ │ ├── logging_config.py # Structured logging
│ │ │ └── middleware.py # Request logging
│ │ ├── models/ # SQLAlchemy models
│ │ │ ├── user.py # is_team_admin field added
│ │ │ ├── team.py
│ │ │ ├── tree.py # JSONB tree_structure + category_id, tags
│ │ │ ├── session.py # JSONB path_taken, decisions
│ │ │ ├── attachment.py
│ │ │ ├── invite_code.py
│ │ │ ├── category.py # TreeCategory model
│ │ │ ├── tag.py # TreeTag model
│ │ │ ├── folder.py # UserFolder model
│ │ │ ├── step_category.py # StepCategory model (NEW)
│ │ │ └── step_library.py # StepLibrary, StepRating, StepUsageLog (NEW)
│ │ └── schemas/ # Pydantic schemas
│ ├── alembic/ # Database migrations
│ ├── scripts/
│ │ ├── seed_data.py
│ │ └── seed_trees.py # 7 comprehensive trees
│ ├── tests/ # pytest integration tests
│ ├── docker-compose.yml # PostgreSQL container
│ ├── requirements.txt
│ └── .env # Environment variables
│
├── brand-assets/ # Source brand SVGs and guides
│
├── frontend/
│ ├── public/
│ │ └── icons/ # Favicon and PWA icons
│ ├── src/
│ │ ├── main.tsx
│ │ ├── App.tsx
│ │ ├── router.tsx
│ │ ├── assets/brand/ # Brand logos (SVG)
│ │ ├── api/ # Axios API client
│ │ │ ├── client.ts # Axios instance with refresh queue interceptor
│ │ │ ├── auth.ts
│ │ │ ├── trees.ts
│ │ │ └── sessions.ts
│ │ ├── hooks/ # Custom React hooks (useKeyboardShortcuts)
│ │ ├── store/
│ │ │ ├── authStore.ts # Zustand auth state
│ │ │ ├── themeStore.ts # Dark/light theme
│ │ │ ├── treeEditorStore.ts # Tree editor state (immer + zundo)
│ │ │ └── userPreferencesStore.ts # User preferences (NEW)
│ │ ├── components/
│ │ │ ├── common/ # Modal, ErrorBoundary, ThemeToggle
│ │ │ ├── layout/ # AppLayout, ProtectedRoute
│ │ │ ├── tree-editor/ # Tree editor components
│ │ │ ├── tree-preview/ # Visual tree preview
│ │ │ ├── step-library/ # Step library browser, forms, modals
│ │ │ ├── session/ # Session modals, scratchpad floating overlay
│ │ │ └── ui/ # MarkdownContent
│ │ ├── pages/
│ │ │ ├── LoginPage.tsx
│ │ │ ├── RegisterPage.tsx
│ │ │ ├── TreeLibraryPage.tsx
│ │ │ ├── TreeNavigationPage.tsx # Core feature
│ │ │ ├── TreeEditorPage.tsx
│ │ │ ├── SessionHistoryPage.tsx
│ │ │ ├── SessionDetailPage.tsx
│ │ │ └── SettingsPage.tsx # User preferences (NEW)
│ │ ├── types/ # TypeScript interfaces
│ │ └── lib/utils.ts # cn() utility for Tailwind
│ ├── package.json
│ ├── tailwind.config.js
│ └── vite.config.ts
│
├── CLAUDE.md # This file
├── CURRENT-STATE.md # Quick status reference
├── LESSONS-LEARNED.md # Bugs and fixes (READ THIS!)
├── PROGRESS.md # Detailed progress log
├── REBRAND-IMPLEMENTATION-GUIDE.md # Patherly → ResolutionFlow rebrand guide
├── docs/plans/ # Design docs & implementation plans (YYYY-MM-DD-<topic>-design.md / -implementation.md)
├── 01-PROJECT-OVERVIEW.md # Vision and goals
├── 02-TECHNICAL-ARCHITECTURE.md # System design, API specs
├── 03-DEVELOPMENT-ROADMAP.md # Phases and timeline
├── 04-FEATURE-SPECIFICATIONS.md # Feature details
├── 05-QUESTIONS-AND-ACTION-ITEMS.md
└── PHASE-2.5-PERSONAL-BRANCHING.md # Future feature spec
Environment Variables
Backend (.env)
Required in backend/.env:
# Application
APP_NAME=ResolutionFlow
DEBUG=true # Set false in production
# Database (matches docker-compose.yml)
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/patherly
DATABASE_URL_SYNC=postgresql://postgres:postgres@localhost:5432/patherly
# JWT Settings - CHANGE THESE IN PRODUCTION
SECRET_KEY=<generate with: openssl rand -hex 32>
ACCESS_TOKEN_EXPIRE_MINUTES=30
REFRESH_TOKEN_EXPIRE_MINUTES=10080
# Auth
REQUIRE_INVITE_CODE=true # Set false to allow open registration
Railway-specific (production):
ALLOW_RAILWAY_ORIGINS=true # Enables CORS for PR environments
Frontend (.env.local - optional)
VITE_API_URL=http://localhost:8000 # Override API URL
Development Commands
Start Development Environment
# Terminal 1: Start PostgreSQL
docker start patherly_postgres
# Terminal 2: Backend (from project root)
cd backend
# Windows:
.\venv\Scripts\Activate
# Linux/Mac:
source venv/bin/activate
uvicorn app.main:app --reload
# Terminal 3: Frontend (from project root)
cd frontend
npm run dev
URLs
- Frontend: http://localhost:5173
- Backend API: http://localhost:8000
- API Docs: http://localhost:8000/api/docs
Run Tests
# From project root
cd backend
# First time only: create test database
docker exec -it patherly_postgres psql -U postgres -c "CREATE DATABASE patherly_test;"
# Install test dependencies (if not already installed)
pip install -r requirements-dev.txt
# Run tests
pytest
Frontend Operations
# From project root
cd frontend
# Build for production
npm run build
# Preview production build
npm run preview
# Lint code
npm run lint
Run Seed Scripts
# From project root
cd backend
pip install httpx # Required for seed scripts
python -m scripts.seed_trees
Database Operations
# From project root
cd backend
# Run migrations
alembic upgrade head
# Create new migration
alembic revision --autogenerate -m "Description"
# Create migration without DB running (manual mode)
alembic revision -m "Description" # Edit migration file manually
# Migration runs when DB is available - safe to commit without testing locally
# Find current migration head (check down_revision in latest file)
# Chain mixes numeric IDs (001-008) and hex hashes (e.g., 4cdb5cba1aff)
# Always inspect the actual files — don't assume sequential ordering
# Access PostgreSQL (no local psql needed)
docker exec -it patherly_postgres psql -U postgres -d patherly
Critical Lessons Learned
ALWAYS read LESSONS-LEARNED.md before making changes!
DateTime Handling (Critical)
# CORRECT - Always use timezone-aware datetimes
from datetime import datetime, timezone
from sqlalchemy import DateTime
created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
# WRONG - Never use this
datetime.utcnow() # Deprecated, returns naive datetime
React State: Don't Store Object Snapshots
// WRONG - Snapshot won't update when store changes
const [editingNode, setEditingNode] = useState<TreeStructure | null>(null)
// CORRECT - Store ID only, fetch current object each render
const [editingNodeId, setEditingNodeId] = useState<string | null>(null)
const editingNode = editingNodeId ? findNode(editingNodeId) : null
Modal Draft State: Don't Overwrite Store-Managed Fields
// WRONG - Overwrites children with stale snapshot
const handleSave = () => {
updateNode(node.id, draft) // draft.children is stale!
}
// CORRECT - Exclude store-managed fields
const handleSave = () => {
const { children, ...draftWithoutChildren } = draft
updateNode(node.id, draftWithoutChildren)
}
Database Name
- Database name is
patherly(notdecision_tree) - Update
.envif you see the old name
Virtual Environment
- Always check for
(venv)prefix before running pip - Don't use
--break-system-packageswhen venv is active
PostgreSQL NULL Casting for UUID Columns
-- WRONG - PostgreSQL infers NULL as text type
INSERT INTO tree_tags (name, slug, team_id)
SELECT 'tag', 'slug', NULL as team_id -- Error: column is uuid but expression is text
-- CORRECT - Explicitly cast NULL to uuid
INSERT INTO tree_tags (name, slug, team_id)
SELECT 'tag', 'slug', NULL::uuid as team_id -- Works!
Always use NULL::uuid when inserting NULL values into UUID columns in raw SQL.
SQLAlchemy Async: Avoid Lazy Loading on New Objects
# WRONG - Triggers lazy load which fails in async context
new_tree = Tree(...)
db.add(new_tree)
await db.flush()
new_tree.tags.append(tag) # MissingGreenlet error!
# CORRECT - Use direct SQL for junction tables
from app.models.tag import tree_tag_assignments
await db.execute(
tree_tag_assignments.insert().values(
tree_id=new_tree.id,
tag_id=tag.id
)
)
Accessing relationships on newly created objects triggers lazy loading, which fails in async SQLAlchemy. Use direct SQL inserts for junction tables instead.
React Router: Clear Dirty State Before Navigation
// WRONG - Navigation triggers before dirty flag is cleared
const newTree = await treesApi.create(data)
navigate(`/trees/${newTree.id}/edit`) // Blocker fires here!
markSaved() // Too late
// CORRECT - Clear dirty state first
const newTree = await treesApi.create(data)
markSaved() // Clear isDirty first
navigate(`/trees/${newTree.id}/edit`) // Blocker won't fire
When using useBlocker for unsaved changes, always clear the dirty flag before programmatic navigation.
CORS: Include Both allow_origins AND allow_origin_regex
# WRONG - Custom domains ignored when using regex
if settings.ALLOW_RAILWAY_ORIGINS:
app.add_middleware(
CORSMiddleware,
allow_origin_regex=r"https://.*\.up\.railway\.app", # Only matches Railway domains!
# ...
)
# CORRECT - Include both for custom domains + Railway PR environments
if settings.ALLOW_RAILWAY_ORIGINS:
app.add_middleware(
CORSMiddleware,
allow_origins=settings.allowed_origins, # Custom domains like resolutionflow.com
allow_origin_regex=r"https://.*\.up\.railway\.app", # Railway PR domains
# ...
)
When using allow_origin_regex for wildcard patterns, also include allow_origins for explicit custom domains. The regex alone won't match custom domains like resolutionflow.com.
findNode Requires Tree Structure Parameter
// WRONG - Always returns null (structure defaults to undefined)
const node = findNode(nodeId)
// CORRECT - Pass tree structure explicitly
const node = findNode(nodeId, tree?.tree_structure)
// For custom steps, also check:
const customStep = findCustomStep(nodeId)
Frontend/Backend API Path Alignment
Always verify frontend API paths match backend route definitions. Example: backend serves /steps/tags/popular but frontend called /steps/popular-tags. Check backend/app/api/router.py and endpoint files for actual paths.
Custom Step Flow in Tree Navigation
The custom step insertion flow in TreeNavigationPage.tsx chains through multiple modals:
CustomStepModal(create/browse) → 2.PostStepActionModal(save/use/both) → 3.ContinuationModal(pick descendant or build branch) → 4. Custom step view with "Continue to" button
Key state: pendingStep, pendingContinuationNodeId, customBranchMode, branchOriginNodeId
Custom steps are stored in session JSONB (custom_steps field) and referenced by UUID in pathTaken.
findNode() only searches tree structure -- use findCustomStep() for custom step UUIDs.
Token Refresh: Match Frontend/Backend Contract
The refresh endpoint must accept tokens the same way the frontend sends them.
# WRONG - Expects bare string, but frontend sends Authorization header
@router.post("/refresh")
async def refresh_token(refresh_token: str):
payload = decode_token(refresh_token)
# CORRECT - Use dependency that reads from Authorization header
@router.post("/refresh")
async def refresh_token(
payload: Annotated[dict, Depends(get_refresh_token_payload)],
):
The frontend Axios interceptor sends Authorization: Bearer <refresh_token>. The backend must extract it from the header, not expect it as a query/body parameter.
CORS Errors Can Mask Server 500s
When the backend returns a 500 Internal Server Error, CORS headers are not added to the response. The browser reports this as a CORS error, hiding the real cause. Always check backend logs first when debugging CORS issues locally.
Run Migrations Before Local Testing
After cloning or pulling new changes, always run alembic upgrade head before starting the backend. Missing migrations cause 500 errors (e.g., column does not exist) that manifest as CORS errors in the browser.
API Endpoints Reference
Full API documentation: http://localhost:8000/api/docs (interactive OpenAPI/Swagger UI)
Quick reference:
- Auth:
/api/v1/auth/*- register, login, refresh, logout, me - Trees:
/api/v1/trees/*- CRUD, search, categories (supports filters: category_id, tags, folder_id) - Sessions:
/api/v1/sessions/*- start, track, complete, export (markdown/text/html) - Tags:
/api/v1/tags/*- manage tree tags, autocomplete search - Folders:
/api/v1/folders/*- user folders with subfolder hierarchy (max 3 levels deep) - Categories:
/api/v1/categories/*- tree categories (global + team-specific) - Step Categories:
/api/v1/step-categories/*- step categories for library - Step Library:
/api/v1/steps/*- reusable steps, ratings, reviews, full-text search - Invite Codes:
/api/v1/invite-codes/*- admin management
Key constraints:
- Folder hierarchy: max 3 levels deep (root → child → grandchild)
- Step ratings: 1-5 stars with optional review text
- Categories and tags: support both global and team-specific scoping
For detailed parameters, request/response schemas, and examples, visit the API docs.
Data Models
Tree Structure (JSONB)
interface TreeStructure {
id: string
type: 'decision' | 'action' | 'solution'
// Decision nodes
question?: string
help_text?: string
options?: Array<{
id: string
label: string
next_node_id?: string
}>
// Action nodes
title?: string
description?: string
commands?: string[]
next_node_id?: string
// Solution nodes
title?: string
description?: string
steps?: string[]
// All nodes can have children
children?: TreeStructure[]
}
Session Decisions (JSONB)
interface Decision {
node_id: string
question?: string
answer: string
notes?: string
timestamp: string // ISO string, not datetime object
}
Frontend Patterns
State Management
- Auth:
useAuthStore- Zustand with localStorage persistence (includessetTokensfor silent refresh sync) - Theme:
useThemeStore- Dark/light/system preference - Tree Editor:
useTreeEditorStore- Zustand + immer + zundo (undo/redo) - User Preferences:
useUserPreferencesStore- Zustand with localStorage persistence (export format default)
Component Guidelines
- Use
cn()from@/lib/utilsfor Tailwind class merging - Use Lucide icons (no
titleprop - wrap in<span>instead) - Modals: Use fixed header/footer with scrollable body
- Modals: Import and render at end of parent component JSX
- Forms: Show field-level validation errors
- Conditional rendering: Add null checks when node might not exist (
currentNode && currentNode.type)
TypeScript Type Organization
- New type modules: Create in
types/directory (e.g.,types/step.ts) - Export from
types/index.tswithexport * from './modulename' - Import types using
import type { Type } from '@/types'(type-only import)
API Client Pattern
import api from '@/api/client'
// Token refresh handled automatically by interceptor
// Concurrent 401s are queued — only one refresh request fires at a time
// On refresh failure, user is logged out and redirected to /login
const response = await api.get('/api/v1/trees')
Floating Overlay Pattern (Scratchpad)
The scratchpad uses position: fixed with an onOpenChange callback so the parent page can adjust layout:
// Child: ScratchpadSidebar.tsx
onOpenChange?: (isOpen: boolean) => void
// Fires when collapsed state changes, parent uses it to add/remove padding
// Parent: TreeNavigationPage.tsx
const [scratchpadOpen, setScratchpadOpen] = useState(...)
<div className={cn('...', scratchpadOpen && 'pr-[440px]')}>
<div className="mx-auto max-w-4xl"> {/* centers in available space */}
Position overlay at right-2 (not right-0) so it sits inside the page scrollbar, and use full rounded-lg (not rounded-l-lg).
Common Tasks
Adding a New API Endpoint
- Create route in
backend/app/api/endpoints/ - Add to router in
backend/app/api/router.py - Create/update Pydantic schemas in
backend/app/schemas/ - Add tests in
backend/tests/ - Update API client in
frontend/src/api/
Adding a New API Client Module (Frontend)
- Create types in
frontend/src/types/modulename.ts - Export types from
frontend/src/types/index.ts - Create API client in
frontend/src/api/modulename.tsfollowing existing pattern - Export from
frontend/src/api/index.ts - Import in components:
import { moduleApi } from '@/api'
Adding a New Page
- Create page component in
frontend/src/pages/ - Add route in
frontend/src/router.tsx - Add navigation link in
AppLayout.tsxif needed
Modifying Database Schema
- Update model in
backend/app/models/ - Create migration:
alembic revision --autogenerate -m "description" - Review generated migration file
- Apply:
alembic upgrade head
Coding Standards
Python (Backend)
- Use type hints everywhere
- Use async/await for database operations
- Use Pydantic for validation
- Log with correlation IDs for tracing
- Always use
DateTime(timezone=True)for timestamps
TypeScript (Frontend)
- Enable strict mode (when ready)
- Use TypeScript interfaces for all data structures
- Prefer
constoverlet - Use functional components with hooks
- Extract reusable logic into custom hooks
Git
- Commit message format:
type: description - Types:
feat,fix,refactor,docs,test,chore - Always include
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> - Always create a feature branch BEFORE committing new work (not retroactively after committing to main)
- PR workflow:
git checkout -b feat/feature-name→ commit →git push -u origin feat/feature-name→gh pr create
Commit Strategy for Large Features
- Break implementation into logical phases (foundation → components → integration)
- Commit after each phase with
npm run buildvalidation - Phase commits enable easier debugging and rollback
- Example: API clients (Phase 1) → UI components (Phase 2) → Page integration (Phase 3)
Future Roadmap
Phase 2.5 (In Progress)
- ✅ Step Categories database and API
- ✅ Step Library database schema (step_library, step_ratings, step_usage_log)
- ✅ Step Library CRUD API with search and ratings
- ✅ User Preferences (Settings page, export format default)
- 🔲 Step Library browser component (frontend)
- 🔲 Add Custom Step button in tree navigation
- 🔲 Custom step creation modal
- 🔲 Personal tree branching (add custom steps during sessions)
- 🔲 Tree forking and sharing
Phase 3 (Planned)
- File attachments (screenshots, logs)
- Offline mode (Service Workers + IndexedDB)
- Client context system
- Analytics dashboard
Phase 4 (Planned)
- API & integrations (ConnectWise, Kaseya)
- PowerShell automation execution
- Enterprise features (SSO, white-labeling)
Troubleshooting
Backend won't start
- Check Docker:
docker ps- ispatherly_postgresrunning? - Check
.env- is DATABASE_URL correct (patherlynotdecision_tree)? - Check venv: is
(venv)prefix showing?
Frontend compile errors
- Check
frontend/src/lib/utils.tsexists (providescn()function) - Run
npm installto ensure dependencies are installed
Tests failing
- Create test database:
docker exec -it patherly_postgres psql -U postgres -c "CREATE DATABASE patherly_test;" - Install dev deps:
pip install -r requirements-dev.txt - Ensure pytest-asyncio version:
pip install pytest-asyncio==0.24.0
API 500 errors
- Check server logs for datetime errors (timezone issue)
- Ensure all datetimes use
datetime.now(timezone.utc)
Git Patterns
Always gitignore:
# Secrets and local config
backend/.env
.claude.local.md
.claude/settings.local.json
# Dependencies
backend/venv/
frontend/node_modules/
# Build artifacts
frontend/dist/
backend/**/__pycache__/
*.pyc
# Railway CLI (local tooling only)
/node_modules/
/package.json
/package-lock.json
# IDE
.vscode/
.idea/
*.swp
Quick Reference
| What | Where |
|---|---|
| API Docs | http://localhost:8000/api/docs |
| Current Status | CURRENT-STATE.md |
| Bug Fixes | LESSONS-LEARNED.md |
| Feature Specs | 04-FEATURE-SPECIFICATIONS.md |
| Phase 2.5 Spec | PHASE-2.5-PERSONAL-BRANCHING.md |
| Rebrand Guide | REBRAND-IMPLEMENTATION-GUIDE.md |
Railway Deployment
Production
- Frontend: https://resolutionflow.com
- Backend: https://api.resolutionflow.com
- Database: Railway-managed PostgreSQL
- Deploys automatically on push to
main
PR Environments
Railway creates isolated preview environments for each pull request.
Workflow:
- Create feature branch:
git checkout -b feature/my-feature - Make changes, commit, push to origin
- Open Pull Request on GitHub
- Railway auto-creates preview environment (backend + frontend + DB)
- Generate domains manually in Railway dashboard:
- Switch to the PR environment
- Click on each service → Settings → Networking → Generate Domain
- Set
VITE_API_URLon frontend service to point to the PR backend URL- IMPORTANT: Must include
https://prefix (e.g.,https://patherly-patherly-pr-24.up.railway.app)
- IMPORTANT: Must include
- Redeploy frontend if needed
- Test at preview URLs
- Merge PR → auto-deploys to production
- Railway cleans up PR environment after merge
Environment Variables:
- PR environments inherit from
productionbase environment REQUIRE_INVITE_CODE=trueis inherited (create invite codes in PR DB if needed)DATABASE_URLis auto-provided for isolated PR databaseALLOW_RAILWAY_ORIGINS=true(shared variable) - enables CORS for all*.up.railway.apporigins
Notes:
- Each PR gets a fresh database - no existing users/trees
- Migrations run automatically via
releaseCommand - Domains must be generated manually for each PR service
Debug Endpoints (available in PR environments):
/debug/cors- Check CORS configuration (allow_railway_origins, cors_mode)
Railway CLI (Local)
The Railway CLI can be installed locally for managing deployments, viewing logs, and checking service status.
Setup on a new machine:
# 1. Install Railway CLI via npm (in project root)
cd C:\Dev\Projects\patherly
npm init -y
npm install @railway/cli
# 2. Login to Railway
./node_modules/.bin/railway login
# 3. Link to the project
./node_modules/.bin/railway link
# Select: selfless-grace → production
Common Commands:
# Check service status
./node_modules/.bin/railway service status --service patherly
./node_modules/.bin/railway service status --all
# View logs
./node_modules/.bin/railway service logs --service patherly
# Check environment variables
./node_modules/.bin/railway variables --service patherly
# Deploy new code (from backend directory)
cd backend && ../node_modules/.bin/railway up --service patherly
# Redeploy existing build
./node_modules/.bin/railway service redeploy --service patherly --yes
Services:
patherly- Backend APIhopeful-liberation- FrontendPostgres- Database
Note: The node_modules/, package.json, and package-lock.json files for the Railway CLI are gitignored (local tooling only).
Contact
Primary User: Michael Chihlas Communication: GitHub Issues / Direct chat