Skip to content

Latest commit

 

History

History
239 lines (176 loc) · 10.4 KB

File metadata and controls

239 lines (176 loc) · 10.4 KB

CLAUDE.md - Protocol Guide Project Configuration

This file provides project context for AI assistants working with Protocol Guide.

Project Overview

Protocol Guide is a national EMS protocol library with jurisdiction-aware RAG (Retrieval-Augmented Generation). It ingests clinical protocols from Local Emergency Medical Services Agencies (LEMSAs), state EMS offices, and regional medical directors across the United States, then serves them through a vector-search pipeline scoped to the user's jurisdiction.

Core constraint: EMS protocols are not national. They are set by local medical authorities, and a paramedic in one county follows different standing orders than one in the adjacent county. Every search must resolve the user's jurisdiction and scope results accordingly.

Key Value Proposition: Find the right protocol for your agency in 2 seconds.

EMS Domain Model

Jurisdiction Hierarchy: United States → State (53) → LEMSA/Agency (2,738) → County → Protocol Chunks (58,000+)

Critical Concepts:

  • LEMSA — Local EMS Agency that publishes clinical protocols (manus_agencies table)
  • County-Agency Mapping — Bridge table resolving county → agency_id for scoped search
  • Protocol Chunk — 400-1800 char vector-embedded segment (manus_protocol_chunks, 1536 dim)
  • Jurisdiction-Scoped Search — User county → county_agency_mapping → scoped vector search → Claude RAG with agency's protocols only

EMS Abbreviations: Query normalizer expands 150+ terms (VF→Ventricular Fibrillation, STEMI→ST-Elevation Myocardial Infarction, RSI→Rapid Sequence Intubation, etc.)

Full details: docs/EMS_ARCHITECTURE.md — Jurisdiction model, ingestion pipeline (7 steps: PDF discovery → download → text extraction → chunking → embedding → database insert), search pipeline, complete abbreviation reference.

Architecture

Frontend: Expo 54 + React Native Web + Expo Router + NativeWind (Tailwind) Backend: Express + tRPC 11.7 (end-to-end typesafe) Database: Supabase PostgreSQL + pgvector (vector search) + Drizzle ORM AI: Claude Haiku 4.5 / Sonnet 4.6 (Anthropic) + Voyage AI embeddings (voyage-large-2, 1536 dim) Auth: Supabase Auth (Google/Apple OAuth) Payments: Stripe subscriptions Hosting: Netlify (auto-deploy from main) + PWA with offline caching Caching: Upstash Redis (query cache, rate limits)

Full architecture diagrams: docs/BACKEND.md, docs/FRONTEND.md, docs/DATABASE.md

Key Files

Frontend (app/): _layout.tsx — Root layout + providers | (tabs)/index.tsx — Search interface | (tabs)/profile.tsx — User profile | admin/*.tsx — Admin dashboard | oauth/*.tsx — OAuth callbacks

Backend (server/): _core/index.ts — Express entry | _core/trpc.ts — tRPC config | _core/claude.ts — Claude SDK + model routing | _core/embeddings/ — Voyage AI + pgvector | _core/rag/ — RAG pipeline | _core/ems-query-normalizer.ts — 150+ abbreviation expansion | routers/search.ts — Semantic search | routers/query.ts — Protocol queries | routers/auth.ts — Authentication | routers/user.ts — User profile | routers/subscription.ts — Stripe | routers/voice.ts — Voice transcription

Ingestion (scripts/): ingest-ca-protocols.ts — CA LEMSA orchestrator | lib/pdf-url-discoverer.ts — PDF crawler | lib/pdf-downloader.ts — PDF cache | lib/protocol-extractor.ts — Text extraction

Hooks (hooks/): use-auth.ts | use-protocol-search.ts | use-voice-input.ts | use-offline-cache.ts | use-favorites.ts

Libraries (lib/): trpc.ts | supabase.ts | offline-cache.ts | tier-helpers.ts | ems-terminology.ts

Database (drizzle/): schema.ts — Table definitions | migrations/*.sql — Auto-generated migrations

Full file reference: docs/BACKEND.md, docs/FRONTEND.md

Common Development Tasks

Starting Development

pnpm install
pnpm dev          # Server :3001 + Web :8081
pnpm dev:server   # Backend only
pnpm dev:metro    # Frontend only

Database Operations

pnpm db:push              # Generate and apply migrations
npx drizzle-kit studio    # View database in Studio

Testing

pnpm test                 # Unit tests (Vitest)
pnpm test:coverage        # With coverage
pnpm test:integration     # Integration tests (requires DB)
pnpm test:e2e             # E2E tests (Playwright)
pnpm test:e2e:visual      # Visual regression

Code Quality

pnpm check    # TypeScript type checking
pnpm lint     # ESLint
pnpm format   # Prettier

Building

pnpm build        # Build server bundle
pnpm build:web    # Build PWA for deployment

Environment Variables

Required variables (see .env.example):

# Core
DATABASE_URL=postgresql://...
NODE_ENV=development

# Supabase (Auth + Vector Search for protocol chunks)
SUPABASE_URL=https://xxx.supabase.co
SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ...

# AI/ML
ANTHROPIC_API_KEY=sk-ant-...    # Claude Haiku 4.5 / Sonnet 4.6
VOYAGE_API_KEY=voyage-...        # Embeddings (voyage-large-2, 1536 dim)

# Payments
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...

# Caching (optional)
UPSTASH_REDIS_REST_URL=https://...
UPSTASH_REDIS_REST_TOKEN=...

Code Standards

File Size Limit

  • 500 lines maximum per file
  • Split large files into focused modules

TypeScript

  • Explicit types everywhere (no any)
  • Use Zod for runtime validation
  • Prefer interfaces over type aliases for objects

Naming Conventions

  • Files: kebab-case (use-auth.ts, ems-query-normalizer.ts)
  • Components: PascalCase (SearchBar.tsx, AgencyModal.tsx)
  • Functions: camelCase (handleSearch, normalizeEmsQuery)
  • Constants: UPPER_SNAKE_CASE (MAX_RESULTS, CHUNK_CONFIG)
  • Types/Interfaces: PascalCase (UserTier, LEMSAConfig, NormalizedQuery)

tRPC Patterns

  • publicProcedure — No auth required
  • publicRateLimitedProcedure — Public with rate limiting
  • protectedProcedure — Requires authentication
  • csrfProtectedProcedure — Requires CSRF token
  • rateLimitedProcedure — Auth + rate limiting

Error Handling

  • Use TRPCError for API errors
  • Always include error codes
  • Log errors with context

LLM Architecture

Claude Code Agent Layer

Claude Code agents for this project run on Claude Opus 4.6 for code analysis, planning, and implementation.

Production Model Routing (User-Facing API)

Free tier  → Claude Haiku 4.5 only (~$0.0003/query)
Pro simple → Claude Haiku 4.5 (~$0.0003/query)
Pro complex → Claude Sonnet 4.6 (~$0.003/query)

Updated 2026-02-17 for Sonnet 4.6 release

Complexity triggers (routes Pro users to Sonnet): differential diagnosis, multi-condition interactions, pediatric edge cases, explanation requests, atypical presentations.

Jurisdiction-Aware Search Pipeline (6 Steps)

  1. Query Normalization — Expand 150+ EMS abbreviations, classify intent, detect emergent indicators
  2. County→Agency Resolutioncounty_agency_mapping resolves user's county to agency_id
  3. Embedding Generation — Voyage AI voyage-large-2 (1536 dim)
  4. Scoped Vector Search — pgvector cosine similarity WHERE agency_id = X
  5. Re-ranking — Term frequency, synonym matching, context boost (+15 agency, +5 state)
  6. Response Generation — Claude RAG with retrieval-only constraint, mandatory [Protocol #XXX] citations

Safety-Critical Queries:

  • Medication dosing → Multi-query fusion (3 variations, RRF merge), threshold 0.38
  • Contraindication checks → Highest priority (100), enhanced accuracy mode
  • Emergent patterns (cardiac arrest, anaphylaxis, airway obstruction) → Always enhanced processing
  • Pediatric + medication → Weight-based dosing alerts, enhanced accuracy regardless of tier

Full LLM architecture: docs/EMS_ARCHITECTURE.md, docs/BACKEND.md

Deployment

Production (Netlify): Auto-deploys from main branch, Netlify Functions for serverless backend, edge functions for auth middleware

PWA Features: Service worker caching for offline protocol access, install prompt for home screen, standalone mode, background sync

Full deployment guide: DEPLOYMENT.md

Testing Strategy

Unit Tests (Vitest): Server utilities (query normalizer, chunker, scoring), React hooks (search, auth, offline), business logic in lib/

Integration Tests: Database operations (county→agency mapping, protocol search), tRPC router endpoints, authentication flows

E2E Tests (Playwright): Critical user journeys (search, jurisdiction selection, subscription), search functionality (jurisdiction-scoped results), visual regression tests

Troubleshooting

"Database connection failed"

  • Check DATABASE_URL in .env
  • Run pnpm db:push to apply migrations

"Search returning no results"

  • Verify VOYAGE_API_KEY is set
  • Check Supabase pgvector extension is enabled
  • Verify the user's county has a county_agency_mapping row
  • Check manus_protocol_chunks has data for the resolved agency_id

"Wrong jurisdiction protocols"

  • Check county_agency_mapping maps the county to the correct agency_id
  • Verify manus_agencies has the correct state_code and name
  • Multi-county LEMSAs: all counties in a LEMSA should map to the same agency_id

"Claude API error"

  • Verify ANTHROPIC_API_KEY is set
  • Check API rate limits

"Auth not working"

  • Clear browser cookies
  • Check Supabase URL/keys
  • Verify OAuth redirect URLs in Supabase dashboard

Related Documentation