# 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.tsx` and `LoginPage.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 in `tailwind.config.js` and `index.css`) - **Rebrand guide:** [REBRAND-IMPLEMENTATION-GUIDE.md](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 - **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_library` table for reusable troubleshooting steps - `step_ratings` table for user ratings/reviews - `step_usage_log` table 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 - **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 ### 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 interceptors │ │ │ ├── auth.ts │ │ │ ├── trees.ts │ │ │ └── sessions.ts │ │ ├── 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 │ │ │ └── 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/ # Implementation plans ├── 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`:** ```bash # 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= ACCESS_TOKEN_EXPIRE_MINUTES=30 REFRESH_TOKEN_EXPIRE_MINUTES=10080 # Auth REQUIRE_INVITE_CODE=true # Set false to allow open registration ``` **Railway-specific (production):** ```bash ALLOW_RAILWAY_ORIGINS=true # Enables CORS for PR environments ``` ### Frontend (.env.local - optional) ```bash VITE_API_URL=http://localhost:8000 # Override API URL ``` --- ## Development Commands ### Start Development Environment ```powershell # 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 ```powershell # 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 ```powershell # 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 ```powershell # From project root cd backend pip install httpx # Required for seed scripts python -m scripts.seed_trees ``` ### Database Operations ```powershell # 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 # Access PostgreSQL (no local psql needed) docker exec -it patherly_postgres psql -U postgres -d patherly ``` --- ## Critical Lessons Learned **ALWAYS read [LESSONS-LEARNED.md](LESSONS-LEARNED.md) before making changes!** ### DateTime Handling (Critical) ```python # 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 ```tsx // WRONG - Snapshot won't update when store changes const [editingNode, setEditingNode] = useState(null) // CORRECT - Store ID only, fetch current object each render const [editingNodeId, setEditingNodeId] = useState(null) const editingNode = editingNodeId ? findNode(editingNodeId) : null ``` ### Modal Draft State: Don't Overwrite Store-Managed Fields ```tsx // 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` (not `decision_tree`) - Update `.env` if you see the old name ### Virtual Environment - Always check for `(venv)` prefix before running pip - Don't use `--break-system-packages` when venv is active ### PostgreSQL NULL Casting for UUID Columns ```sql -- 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 ```python # 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 ```tsx // 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 ```python # 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 ```tsx // 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: 1. `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. --- ## 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) ```typescript 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) ```typescript 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 - **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/utils` for Tailwind class merging - Use Lucide icons (no `title` prop - wrap in `` 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.ts` with `export * from './modulename'` - Import types using `import type { Type } from '@/types'` (type-only import) ### API Client Pattern ```typescript import api from '@/api/client' // Token refresh handled automatically by interceptor const response = await api.get('/api/v1/trees') ``` --- ## Common Tasks ### Adding a New API Endpoint 1. Create route in `backend/app/api/endpoints/` 2. Add to router in `backend/app/api/router.py` 3. Create/update Pydantic schemas in `backend/app/schemas/` 4. Add tests in `backend/tests/` 5. Update API client in `frontend/src/api/` ### Adding a New API Client Module (Frontend) 1. Create types in `frontend/src/types/modulename.ts` 2. Export types from `frontend/src/types/index.ts` 3. Create API client in `frontend/src/api/modulename.ts` following existing pattern 4. Export from `frontend/src/api/index.ts` 5. Import in components: `import { moduleApi } from '@/api'` ### Adding a New Page 1. Create page component in `frontend/src/pages/` 2. Add route in `frontend/src/router.tsx` 3. Add navigation link in `AppLayout.tsx` if needed ### Modifying Database Schema 1. Update model in `backend/app/models/` 2. Create migration: `alembic revision --autogenerate -m "description"` 3. Review generated migration file 4. 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 `const` over `let` - 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 ` ### Commit Strategy for Large Features - Break implementation into logical phases (foundation → components → integration) - Commit after each phase with `npm run build` validation - 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 1. Check Docker: `docker ps` - is `patherly_postgres` running? 2. Check `.env` - is DATABASE_URL correct (`patherly` not `decision_tree`)? 3. Check venv: is `(venv)` prefix showing? ### Frontend compile errors 1. Check `frontend/src/lib/utils.ts` exists (provides `cn()` function) 2. Run `npm install` to ensure dependencies are installed ### Tests failing 1. Create test database: `docker exec -it patherly_postgres psql -U postgres -c "CREATE DATABASE patherly_test;"` 2. Install dev deps: `pip install -r requirements-dev.txt` 3. Ensure pytest-asyncio version: `pip install pytest-asyncio==0.24.0` ### API 500 errors 1. Check server logs for datetime errors (timezone issue) 2. 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](CURRENT-STATE.md) | | Bug Fixes | [LESSONS-LEARNED.md](LESSONS-LEARNED.md) | | Feature Specs | [04-FEATURE-SPECIFICATIONS.md](04-FEATURE-SPECIFICATIONS.md) | | Phase 2.5 Spec | [PHASE-2.5-PERSONAL-BRANCHING.md](PHASE-2.5-PERSONAL-BRANCHING.md) | | Rebrand Guide | [REBRAND-IMPLEMENTATION-GUIDE.md](REBRAND-IMPLEMENTATION-GUIDE.md) | --- ## Railway Deployment ### Production - **Frontend:** - **Backend:** - **Database:** Railway-managed PostgreSQL - Deploys automatically on push to `main` ### PR Environments Railway creates isolated preview environments for each pull request. **Workflow:** 1. Create feature branch: `git checkout -b feature/my-feature` 2. Make changes, commit, push to origin 3. Open Pull Request on GitHub 4. Railway auto-creates preview environment (backend + frontend + DB) 5. **Generate domains manually** in Railway dashboard: - Switch to the PR environment - Click on each service → Settings → Networking → Generate Domain 6. **Set `VITE_API_URL`** on frontend service to point to the PR backend URL - **IMPORTANT:** Must include `https://` prefix (e.g., `https://patherly-patherly-pr-24.up.railway.app`) 7. Redeploy frontend if needed 8. Test at preview URLs 9. Merge PR → auto-deploys to production 10. Railway cleans up PR environment after merge **Environment Variables:** - PR environments inherit from `production` base environment - `REQUIRE_INVITE_CODE=true` is inherited (create invite codes in PR DB if needed) - `DATABASE_URL` is auto-provided for isolated PR database - `ALLOW_RAILWAY_ORIGINS=true` (shared variable) - enables CORS for all `*.up.railway.app` origins **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:** ```powershell # 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:** ```powershell # 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 API - `hopeful-liberation` - Frontend - `Postgres` - 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