diff --git a/CLAUDE.md b/CLAUDE.md index 97213db4..82294687 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,7 +1,7 @@ # CLAUDE.md - Patherly Project Context > **Purpose:** This file provides Claude Code with essential context for working on the Patherly project. -> **Last Updated:** February 3, 2026 +> **Last Updated:** February 3, 2026 (improved by claude-md-improver) --- @@ -25,6 +25,7 @@ - **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 @@ -63,9 +64,11 @@ - Rating/review system with verified use tracking ### What's In Progress + - Step Library frontend UI (Phase 2.5 continuation) ### Deployment + - **Production:** Railway (app.patherly.com / api.patherly.com) - **PR Environments:** Enabled - auto-created for each pull request @@ -74,6 +77,7 @@ ## Tech Stack ### Backend + - **Framework:** Python FastAPI - **Database:** PostgreSQL 16 (async via SQLAlchemy 2.0 + asyncpg) - **Migrations:** Alembic @@ -81,6 +85,7 @@ - **Validation:** Pydantic v2 ### Frontend + - **Framework:** React 19 + Vite + TypeScript - **Styling:** Tailwind CSS v3 - **State:** Zustand (with immer + zundo for undo/redo) @@ -183,6 +188,42 @@ patherly/ --- +## Environment Variables + +### Backend (.env) + +**Required in `backend/.env`:** +```bash +# Application +APP_NAME=Patherly +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 @@ -191,40 +232,73 @@ patherly/ # Terminal 1: Start PostgreSQL docker start patherly_postgres -# Terminal 2: Backend -cd C:\Dev\Projects\patherly\patherly\backend +# 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 -cd C:\Dev\Projects\patherly\patherly\frontend +# 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 -cd C:\Dev\Projects\patherly\patherly\backend -.\venv\Scripts\Activate +# 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 ``` -### Run Seed Scripts +### Frontend Operations + ```powershell -cd C:\Dev\Projects\patherly\patherly\backend -.\venv\Scripts\Activate +# 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 -# Run migrations +# From project root cd backend + +# Run migrations alembic upgrade head # Create new migration @@ -241,6 +315,7 @@ docker exec -it patherly_postgres psql -U postgres -d patherly **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 @@ -253,6 +328,7 @@ 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) @@ -263,6 +339,7 @@ const editingNode = editingNodeId ? findNode(editingNodeId) : null ``` ### Modal Draft State: Don't Overwrite Store-Managed Fields + ```tsx // WRONG - Overwrites children with stale snapshot const handleSave = () => { @@ -277,14 +354,17 @@ const handleSave = () => { ``` ### 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) @@ -297,6 +377,7 @@ 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(...) @@ -316,6 +397,7 @@ await db.execute( 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) @@ -329,118 +411,58 @@ 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 app.patherly.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 `app.patherly.com`. + --- ## API Endpoints Reference -### Authentication -``` -POST /api/v1/auth/register - Register (requires invite code by default) -POST /api/v1/auth/login - Login (form data) -POST /api/v1/auth/login/json - Login (JSON body) -POST /api/v1/auth/refresh - Refresh access token -GET /api/v1/auth/me - Get current user -POST /api/v1/auth/logout - Logout -``` +**Full API documentation:** http://localhost:8000/api/docs (interactive OpenAPI/Swagger UI) -### Trees -``` -GET /api/v1/trees - List trees (filters: category_id, tags, folder_id) -POST /api/v1/trees - Create tree (supports tags, category_id) -GET /api/v1/trees/categories - Get legacy string categories -GET /api/v1/trees/search - Full-text search -GET /api/v1/trees/{id} - Get tree (includes tags, category_info) -PUT /api/v1/trees/{id} - Update tree -DELETE /api/v1/trees/{id} - Soft delete -``` +**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 -### Categories (NEW) -``` -GET /api/v1/categories - List categories (global + user's team) -POST /api/v1/categories - Create category (team/global admin) -GET /api/v1/categories/{id} - Get category -PUT /api/v1/categories/{id} - Update category -DELETE /api/v1/categories/{id} - Soft delete category -``` +**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 -### Tags (NEW) -``` -GET /api/v1/tags - List tags (global + user's team) -GET /api/v1/tags/search - Autocomplete search -POST /api/v1/tags - Create tag -GET /api/v1/tags/{id} - Get tag -GET /api/v1/tags/trees/{id} - Get tree's tags -POST /api/v1/tags/trees/{id} - Add tags to tree -PUT /api/v1/tags/trees/{id} - Replace tree's tags -DELETE /api/v1/tags/trees/{id}/{slug} - Remove tag from tree -``` - -### Folders -``` -GET /api/v1/folders - List user's folders (includes parent_id) -POST /api/v1/folders - Create folder (supports parent_id for subfolders) -GET /api/v1/folders/{id} - Get folder -PUT /api/v1/folders/{id} - Update folder (supports moving via parent_id) -DELETE /api/v1/folders/{id} - Delete folder (cascades to subfolders) -POST /api/v1/folders/reorder - Reorder folders -POST /api/v1/folders/{id}/trees - Add tree to folder -GET /api/v1/folders/{id}/trees - Get folder's tree IDs -DELETE /api/v1/folders/{id}/trees/{tree_id} - Remove tree from folder -``` - -**Folder hierarchy constraints:** -- Max nesting depth: 3 levels (root → child → grandchild) -- Same folder name allowed under different parents -- Moving folders validates cycle prevention - -### Step Categories (NEW) -``` -GET /api/v1/step-categories - List categories (global + user's team) -POST /api/v1/step-categories - Create category (admin/team_admin) -GET /api/v1/step-categories/{id} - Get category -PUT /api/v1/step-categories/{id} - Update category -DELETE /api/v1/step-categories/{id} - Soft delete category -``` - -### Step Library (NEW) -``` -GET /api/v1/steps - List steps (filters: visibility, category_id, tags, min_rating, step_type, sort_by) -POST /api/v1/steps - Create step -GET /api/v1/steps/{id} - Get step details -PUT /api/v1/steps/{id} - Update step (owner/admin) -DELETE /api/v1/steps/{id} - Soft delete step (owner/admin) -GET /api/v1/steps/search - Full-text search (?q=query) -GET /api/v1/steps/tags/popular - Popular tags list - -# Rating endpoints -POST /api/v1/steps/{id}/rate - Rate a step (1-5 stars + optional review) -PUT /api/v1/steps/{id}/rate - Update your rating -DELETE /api/v1/steps/{id}/rate - Remove your rating -GET /api/v1/steps/{id}/reviews - Get reviews for a step -``` - -### Sessions -``` -GET /api/v1/sessions - List user's sessions -POST /api/v1/sessions - Start session -GET /api/v1/sessions/{id} - Get session -PUT /api/v1/sessions/{id} - Update session -POST /api/v1/sessions/{id}/complete - Complete session -POST /api/v1/sessions/{id}/export - Export (md/txt/html) -``` - -### Invite Codes -``` -GET /api/v1/invite-codes - List codes (admin) -POST /api/v1/invite-codes - Create code (admin) -GET /api/v1/invite-codes/validate/{code} - Validate code -``` +For detailed parameters, request/response schemas, and examples, visit the API docs. --- ## Data Models ### Tree Structure (JSONB) + ```typescript interface TreeStructure { id: string @@ -472,6 +494,7 @@ interface TreeStructure { ``` ### Session Decisions (JSONB) + ```typescript interface Decision { node_id: string @@ -487,18 +510,21 @@ interface Decision { ## 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 - Forms: Show field-level validation errors ### API Client Pattern + ```typescript import api from '@/api/client' @@ -536,6 +562,7 @@ const response = await api.get('/api/v1/trees') ## Coding Standards ### Python (Backend) + - Use type hints everywhere - Use async/await for database operations - Use Pydantic for validation @@ -543,6 +570,7 @@ const response = await api.get('/api/v1/trees') - 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` @@ -550,6 +578,7 @@ const response = await api.get('/api/v1/trees') - 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 ` @@ -559,6 +588,7 @@ const response = await api.get('/api/v1/trees') ## 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 @@ -570,12 +600,14 @@ const response = await api.get('/api/v1/trees') - 🔲 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) @@ -585,25 +617,59 @@ const response = await api.get('/api/v1/trees') ## 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 + +# 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 | @@ -619,12 +685,14 @@ const response = await api.get('/api/v1/trees') ## Railway Deployment ### Production + - **Frontend:** https://app.patherly.com - **Backend:** https://api.patherly.com - **Database:** Railway-managed PostgreSQL - Deploys automatically on push to `main` ### PR Environments + Railway creates isolated preview environments for each pull request. **Workflow:** @@ -656,6 +724,54 @@ Railway creates isolated preview environments for each pull request. **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