# CLAUDE.md - Patherly / ResolutionFlow Project Context > **Last Updated:** March 3, 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 glassmorphism with ice-cyan accent gradient (`#06b6d4` → `#22d3ee`). Charcoal backgrounds, frosted-glass cards with `backdrop-filter: blur()`, orchestrated page-load animations, bold display typography. Design doc: [docs/plans/2026-03-03-aesthetic-redesign-design.md](docs/plans/2026-03-03-aesthetic-redesign-design.md) - **Fonts:** Bricolage Grotesque (`font-heading`, headings/titles), IBM Plex Sans (`font-sans`, body text), JetBrains Mono (`font-label`, labels/badges/timestamps) — loaded via Google Fonts - **Logo:** Inline SVG in `BrandLogo.tsx` (decision-tree icon with cyan gradient). Wordmark: "Resolution" in `text-foreground` + "Flow" in `text-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 in `tailwind.config.js` and `index.css`). Glass utilities: `.glass-card` (interactive, `scale(1.02)` hover), `.glass-card-static` (no hover transform), `.active-glow` (breathing cyan shadow) - **Layout:** App shell with persistent sidebar + top bar + main content (CSS Grid). Two fixed atmosphere orbs (cyan top-right, purple bottom-left) behind the shell for ambient glow. See [UI-DESIGN-SYSTEM.md](UI-DESIGN-SYSTEM.md) - **Navigation:** Sidebar nav with type sub-items (All Flows → Troubleshooting / Projects / Maintenance). Pinned flows section for quick access. NO workspace switcher. See [UI-DESIGN-SYSTEM.md](UI-DESIGN-SYSTEM.md) - **Terminology:** User-facing label is "Flows" (not "Trees"). Procedural flows are called "Projects" in the UI. Maintenance flows are called "Maintenance" in the UI. `tree_type` column values unchanged in DB. - **Rebrand guide:** [REBRAND-IMPLEMENTATION-GUIDE.md](REBRAND-IMPLEMENTATION-GUIDE.md) **Component styling rules:** - Primary buttons: `bg-gradient-brand` (cyan `135deg`) with `shadow-lg shadow-primary/20`, hover `opacity-0.9`, active `scale(0.97)` - Secondary buttons: `bg-[rgba(255,255,255,0.04)]` with `border-[rgba(255,255,255,0.06)]`, hover brightens border - Active nav items: `bg-primary/10` background + 3px left cyan gradient accent bar - Stat values: use `text-gradient-brand` for highlighted metrics - Status colors: emerald-400 (success), amber-400 (in-progress), rose-500 (error/critical) - Category dots: 8px colored circles using the category color palette - Tags/badges: `font-label` (JetBrains Mono), small rounded chips with `bg-card border-border` - Cards: `.glass-card` (interactive) or `.glass-card-static` (non-interactive) — semi-transparent bg with `backdrop-filter: blur(16px)`, `border-radius: 16px` - Section labels: `font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground` When adding new pages/components: use "ResolutionFlow" branding, ice-cyan gradient accent theme, `.glass-card` / `.glass-card-static` containers, `text-foreground`/`text-muted-foreground` hierarchy. Primary actions use `bg-gradient-brand`. Pages render inside the app shell (CSS Grid: topbar + sidebar + main). Use "Flows" not "Trees" in all user-facing text; use "Projects" not "Procedures" for procedural flows. Reference [UI-DESIGN-SYSTEM.md](UI-DESIGN-SYSTEM.md) for layout patterns, navigation, and component specs. --- ## Current State - **Phase:** Phase 2.5 - Step Library Foundation (In Progress) - **Backend:** Complete (35+ API endpoints, 100+ integration tests) - **Frontend:** Core features complete, Tree Editor functional - **Database:** PostgreSQL with Docker, 49 migrations - **Detailed status:** [CURRENT-STATE.md](CURRENT-STATE.md) ### What's In Progress - Step Library Frontend UI ### Recently Completed - AI chat session conclusion: outcome tracking, AI-generated ticket summaries, resume flow - Survey completion: email-to-self, thank-you page, admin read/unread/archive/delete management - Survey system: public survey page, admin invite tracking, response viewer with CSV export - Email verification: tokens, banner, admin toggle (platform setting) - AI assistant: in-session copilot panel, standalone chat with RAG - Slate & Ice aesthetic redesign: glassmorphism, brand fonts, orchestrated animations - Account management: profile settings, delete/leave/transfer, chat retention - Maintenance flows: batch session launch, saved target lists, APScheduler scheduling --- ## 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 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, survey, copilot, assistant_chat) │ │ ├── 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 ├── 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`) ```bash 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= ACCESS_TOKEN_EXPIRE_MINUTES=5 REFRESH_TOKEN_EXPIRE_DAYS=7 REQUIRE_INVITE_CODE=true ``` ### Frontend (`frontend/.env.local` - optional) ```bash VITE_API_URL=http://localhost:8000 ``` --- ## Development Commands ```powershell # 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 --log-failed # Failed job logs gh run watch --exit-status # Watch run until complete gh run view --json jobs --jq '.jobs[] | {name: .name, conclusion: .conclusion}' ``` ### URLs - Frontend: http://localhost:5173 - Backend API: http://localhost:8000 - API Docs: http://localhost:8000/api/docs ### 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 ### Top Gotchas (most commonly hit) **1. DateTime Handling — Always timezone-aware:** ```python # 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:** ```python # 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:** ```tsx // 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:** ```tsx const { children, ...draftWithoutChildren } = draft updateNode(node.id, draftWithoutChildren) // Don't overwrite children ``` **5. Multiple FKs to same table — Specify `foreign_keys` on BOTH sides:** ```python author = relationship("User", foreign_keys=[author_id], back_populates="trees") ``` **6. PostgreSQL NULL in UUID columns:** ```sql 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:** ```tsx 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` 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. **16. tree_type has three values:** `'troubleshooting' | 'procedural' | 'maintenance'`. Maintenance reuses the procedural execution engine. Use `PROCEDURAL_TREE_TYPES` set from `core/tree_validation.py` when checking for step-based flow types. **17. Alembic autogenerate can be destructive:** `alembic revision --autogenerate` may generate DROP TABLE/ALTER COLUMN ops for unrelated tables (especially junction tables and tables with complex FKs). Always review generated migrations carefully. Prefer manual migrations for new tables: `alembic revision -m "desc"` then write the `upgrade()`/`downgrade()` by hand. **18. Pydantic partial updates — use `model_fields_set`:** When a PUT/PATCH endpoint needs to distinguish "field not sent" from "field sent as null", check `data.model_fields_set` instead of `data.field is not None`. This allows clients to explicitly clear nullable fields like `description`. **19. `gh pr merge` fails with worktrees:** When `main` is checked out in the primary worktree, `gh pr merge` crashes with "fatal: 'main' is already used by worktree". Use the API directly instead: `gh api repos/ORG/REPO/pulls/N/merge --method PUT --field merge_method=squash` **20. `'answer'` node type in TreeStructure:** `answer` is a transient stub type used only in the canvas editor. Any code that switches on `node.type` (validation, markdown serializer, session nav guard) must explicitly handle `'answer'` or it will hit an unhandled-type error. **21. Test fixtures in `conftest.py`:** Available fixtures are `client` (async HTTP client), `test_db` (async session), `test_user` (registers user, returns email/password/user_data), `auth_headers` (Bearer token dict), `test_tree` (creates a tree), `test_admin` (super_admin user), `admin_auth_headers` (admin Bearer token). There is NO `async_client` or `engineer_token` fixture. **22. Worktree venv path:** Git worktrees (`.worktrees/*/`) do NOT have their own Python venv. Use the main repo's venv: `backend/venv/bin/python -m pytest ...` from the worktree directory. **23. Action nodes navigate via `next_node_id`, not `children`:** `TreeNavigationPage.tsx` handles action nodes by following `next_node_id` only — the `children` array on action nodes is ignored at runtime. Action nodes without `next_node_id` render no "Continue" button (dead end). Any AI generation or manual tree editing must set `next_node_id` on action nodes. **24. Anthropic model IDs:** Both alias (`claude-sonnet-4-6`) and dated (`claude-haiku-4-5-20251001`) forms work. Current default: `claude-sonnet-4-6`. See `backend/app/core/config.py` → `AI_MODEL_ANTHROPIC`. **25. Claude API may wrap JSON responses in markdown fences:** When parsing AI-generated JSON, always strip ` ```json ... ``` ` fences before parsing. See `_strip_markdown_fences()` in `ai_tree_generator_service.py`. **26. `sessionsApi.list` supports `batch_id` filter (added Feb 2026):** Both backend `GET /sessions` and frontend `SessionListParams` accept `batch_id` for querying all sessions in a maintenance batch. Use `sessionsApi.list({ batch_id })` to fetch batch-scoped sessions. **27. Maintenance batch sessions are created all-at-once at launch:** All sessions in a batch exist immediately after `batchLaunchApi.launch()` with `batch_id` + `target_label` set. `started_at` is null until a user begins executing that target — there is no "pending session creation" state. **28. AI tests in CI need `ai_enabled` mock:** Backend tests that hit AI endpoints must mock `settings.ai_enabled = True` via `PropertyMock` since CI has no `ANTHROPIC_API_KEY`. See `tests/test_ai_chat.py` `_enable_ai` fixture pattern. **29. ESLint `no-unused-vars` has no `argsIgnorePattern`:** Underscore-prefixed callback params (e.g., `_count`) still trigger errors. Use `// eslint-disable-next-line @typescript-eslint/no-unused-vars` or remove the param if the type signature allows it. **30. Alembic `env.py` must import all models:** New models won't be discovered by `--autogenerate` unless imported in `alembic/env.py`. If a migration runs but the table isn't created, check imports first. **31. JSONB fields — convert datetime to `.isoformat()` string:** Storing `datetime` objects directly in JSONB columns causes serialization errors. Always convert: `"timestamp": datetime.now(timezone.utc).isoformat()`. **32. Export pipeline order matters:** Generate export → resolve session variables → apply redaction. Redaction MUST run last or sensitive data injected via variables bypasses it. See `sessions.py` export endpoint. **33. Railway: `DATABASE_URL_SYNC` is a property, not an env var:** It derives from `DATABASE_URL` by replacing `postgresql+asyncpg://` with `postgresql://`. Delete any stale `DATABASE_URL_SYNC` variable in Railway dashboard. **34. Railway: run migrations in Docker CMD, not `releaseCommand`:** `releaseCommand` in `railway.toml` is unreliable. Use `CMD alembic upgrade head && uvicorn ...` in the Dockerfile instead. **35. `bcrypt==4.0.1` pinned for passlib compatibility:** Newer bcrypt versions break password hashing. Keep `bcrypt==4.0.1` and `passlib[bcrypt]==1.7.4` pinned in `requirements.txt`. **36. Email validator rejects `.local` TLD:** Pydantic's email validation (via `email-validator`) rejects `.local` as a reserved TLD. Use `example.com` for test/seed user emails. **37. First deployed user needs manual admin promotion:** New users default to `engineer` role. Promote via SQL: `UPDATE users SET role = 'admin' WHERE email = '...';` then re-login for new JWT. **39. Platform settings for feature toggles:** Use `SettingsManager.get("key", db, default=True)` to gate features. Add toggle in `admin/SettingsPage.tsx` (same pattern as `maintenance_mode`). Frontend can check status via a lightweight GET endpoint without auth. **40. Survey public routes:** `SurveyPage` is a public route (no auth). Survey invite tokens are passed as `?token=` query param. Add public pages at top level in `router.tsx` alongside `/login`. **38. Alembic migrations MUST use sequential numbered prefixes:** Check `backend/alembic/versions/` for the highest numbered migration and use the next number. Format: `XXX_descriptive_name.py` (e.g., `040_add_whatever.py`). NEVER use auto-generated revision IDs like `0f1ca2af3647`. Always pass `--rev-id` flag: `alembic revision --autogenerate -m "desc" --rev-id=040`. **41. Assistant chat uses local React state, not a Zustand store:** `AssistantChatPage.tsx` manages `chats`, `activeChatId`, `messages`, `input`, `loading` as `useState`. Don't look for a store when modifying assistant chat. **42. Public pages use raw `fetch()`, not `apiClient`:** Survey, shared sessions, and other no-auth pages call the API directly via `fetch()` with `${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/v1/...`. Don't use `apiClient` — it requires auth tokens and uses relative paths. **43. Adding new email types:** Add static async method to `EmailService` in `core/email.py`. Pattern: check `settings.email_enabled`, import `resend`, build HTML string, call `resend.Emails.send()`, return `bool`. Always fire-and-forget from endpoints (log errors, don't fail the request). **44. AI Chat Builder (Flow Assist) is flow-type-aware:** `ai_chat_service.py` dispatches system prompts, response markers, and validation by `flow_type`. Troubleshooting uses `[TREE_UPDATE]` markers + `validate_generated_tree()`. Procedural/maintenance uses `[STEPS_UPDATE]` markers + `validate_generated_procedural_steps()`. Both support `[METADATA]`; procedural also supports `[INTAKE_FORM]`. **45. Intake form field schema uses `variable_name` and `field_type`:** NOT `name` and `type`. Pattern: `{"variable_name": "server_name", "label": "Server Name", "field_type": "text", "required": true, "display_order": 1}`. Used in `tree_validation.py` and AI prompt examples. **46. `CreateFlowDropdown` uses `AIPromptDialog` for AI-assisted creation:** Opens a simple prompt dialog modal (not a separate page). The dialog starts an AI session, generates the flow, imports it, and navigates to the editor with `{ state: { aiPanelOpen: true, sessionId } }`. The old standalone `/ai/chat` page and `AIFlowBuilderModal` have been removed. **47. Editor-Embedded Flow Assist architecture:** AI assistance is embedded in each editor via `EditorAIPanel` (320px side panel) + `useEditorAI` hook + `ContextMenu`. Tree editor: panel replaces node editor panel (single-panel rule). Procedural editor: panel sits alongside step list (flex layout). Ghost nodes/steps use `_suggestion: true` flag with dashed borders + accept/dismiss controls. Action types (`generate_branch`, `modify_node`, `add_steps`, etc.) route to model tiers via `settings.get_model_for_action()`. Delta responses use `[DELTA]...[/DELTA]` markers. Suggestion audit trail in `ai_suggestions` table. **48. Tree orphan validation uses dynamic root ID:** `treeEditorStore.ts` orphan check compares against `state.treeStructure?.id` (NOT hardcoded `'root'`). AI-generated trees use descriptive root IDs like `"verify-account-exists"`. **49. Full-stack features — verify both ends:** When adding a field to a backend API response (e.g., `working_tree` in `AIChatMessageResponse`), always verify the frontend consumer actually reads and uses it. Similarly, when a frontend hook/component expects data from an API, confirm the backend populates it. Check the full data flow: schema → endpoint → API client → hook → store → UI. **50. Anthropic SDK retry behavior:** Default `max_retries=2` with exponential backoff can cause requests to take 3× the timeout (e.g., 45s × 3 = 135s). Set `max_retries=1` in `AnthropicProvider` to fail fast. Current timeout is `AI_REQUEST_TIMEOUT_SECONDS=120`. **51. AI model tier routing:** `config.py` has `AI_MODEL_TIERS` (fast/standard) and `ACTION_MODEL_MAP` mapping action types to tiers. Use `settings.get_model_for_action(action_type)` to resolve concrete model names. Model IDs must be valid — use alias form (`claude-sonnet-4-6`) not invented dated forms. **52. Mobile scroll-to-top — use `scrollIntoView`, not `window.scrollTo`:** Mobile browsers (iOS Safari, Firefox Android) often ignore `window.scrollTo()`. Use a ref at the top of the page and call `ref.current.scrollIntoView({ behavior: 'smooth', block: 'start' })` instead. Trigger via `useEffect` on the state change (not inline with `setState`) so the DOM has committed before scrolling. **53. Flex height chain — every ancestor must be a flex container for `flex-1` to work:** If a child uses `flex-1` to fill its parent, the parent MUST have `display: flex` (the `flex` class). A missing `flex` on any wrapper div breaks the entire height chain, causing React Flow (and any `h-full` descendant) to collapse to 0 height. Debug with: `let el = document.querySelector('.react-flow'); while(el) { console.log(el.getBoundingClientRect().height, el.className); el = el.parentElement; }`. The break is where height drops to 0. Common symptom: React Flow error `"parent container needs a width and a height"`. **54. React Flow CSS in Tailwind v4 — import in `index.css`, not component JS:** With `@tailwindcss/vite`, importing `@xyflow/react/dist/style.css` inside a component file causes the plugin to process/wrap it in a cascade layer, lowering specificity. Import it in `index.css` instead: `@import '@xyflow/react/dist/style.css';` after `@import 'tailwindcss';`. Override dark theme using `--xy-*` CSS custom properties (e.g., `--xy-edge-stroke-default`) on `.react-flow.dark`, NOT old-style direct property selectors like `.react-flow__edge-path { stroke: ... }`. **55. App shell height chain for full-height pages (tree editor, procedural editor):** The CSS Grid app shell (`app-shell`) → `.main-content` → page component chain must preserve height. `.main-content` is a grid cell with implicit height from `1fr`. Pages using React Flow or other full-height layouts need every wrapper div between `.main-content` and the canvas to either use `flex` + `flex-1` + `min-h-0` or explicit `h-full`. Adding ANY wrapper div (e.g., for animations, transitions) without proper height classes will collapse the canvas to 0. --- ## 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 (Slate & Ice Modern) - **Theme:** Dark glassmorphism with ice-cyan accent (`#06b6d4` → `#22d3ee`). Uses `.glass-card` / `.glass-card-static` for card surfaces - **Backgrounds:** `bg-background` (`#101114` page), glass surfaces use `rgba(24, 26, 31, 0.55)` with `backdrop-filter: blur()` - **Cards:** `.glass-card` (interactive, hover `scale(1.02)` + border/shadow upgrade) or `.glass-card-static` (no hover). Both have `border-radius: 16px`, semi-transparent bg, backdrop blur - **Buttons:** Primary: `bg-gradient-brand text-[#101114] font-semibold rounded-[10px] hover:opacity-90 active:scale-[0.97]`. Secondary: `bg-[rgba(255,255,255,0.04)] border-[rgba(255,255,255,0.06)] text-foreground rounded-[10px]` - **Inputs:** `border-border bg-card text-foreground placeholder:text-muted-foreground` + focus: `focus:border-[rgba(6,182,212,0.3)]` - **Text:** `text-foreground` (`#f8fafc`) → `text-muted-foreground` (`#8891a0`) → `text-[#5a6170]` (dim, for section labels/timestamps) - **Borders:** `var(--glass-border)` (`rgba(255,255,255,0.06)`) default, `rgba(255,255,255,0.12)` on hover - **Hover states:** Border brightens to `rgba(255,255,255,0.12)`, shadow upgrades to `--shadow-float-hover` - **Active/selected:** `bg-primary/10 text-foreground` or cyan gradient accent bar - **Functional colors:** emerald-400 (success), rose-500 (error), amber-400 (warning), blue-400 (info). Always pair with icons, not color alone. - **CSS variables:** Glass system vars (`--glass-bg`, `--glass-border`, `--glass-blur`), shadow system (`--shadow-float`, `--shadow-float-hover`, `--shadow-cyan-glow`), easing (`--ease-out-smooth`) — all in `index.css` `:root` - **Animations:** Orchestrated page-load sequence (slideDown, slideInLeft, fadeInUp cascade, fadeInRight). `breatheGlow` on first stat card. `bellWobble` on notification hover. See design doc for full spec. --- ## Frontend Patterns - **Component guidelines:** Use `cn()` from `@/lib/utils`, Lucide icons (wrap in `` 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:** `CustomStepModal` → `PostStepActionModal` → `ContinuationModal` → 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`. --- ## 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 ` - 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 --- ## 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|` --- ## 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](CURRENT-STATE.md) | | Development Roadmap | [03-DEVELOPMENT-ROADMAP.md](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](04-FEATURE-SPECIFICATIONS.md) | | Design System | [docs/plans/Frontend/DESIGN_SYSTEM_GUIDE.md](docs/plans/Frontend/DESIGN_SYSTEM_GUIDE.md) |