# Claude Code Project Instructions ## CRITICAL RULES (READ FIRST) ### Platform: Linux Only (ADR-014) **ALL tests MUST run in dev container** - Windows results are unreliable. | Test Result | Container | Windows | Status | | ----------- | --------- | ------- | ------------------------ | | Pass | Fail | = | **BROKEN** (must fix) | | Fail | Pass | = | **PASSING** (acceptable) | ```bash # Always test in container podman exec -it flyer-crawler-dev npm test podman exec -it flyer-crawler-dev npm run type-check ``` ### Database Schema Sync **CRITICAL**: Keep these files synchronized: - `sql/master_schema_rollup.sql` (test DB, complete reference) - `sql/initial_schema.sql` (fresh install, identical to rollup) - `sql/migrations/*.sql` (production ALTER TABLE statements) Out-of-sync = test failures. ### Communication Style Ask before assuming. Never assume: - Steps completed / User knowledge / External services configured / Secrets created --- ## Session Startup Checklist 1. **Memory**: `mcp__memory__read_graph` - Recall project context, credentials, known issues 2. **Git**: `git log --oneline -10` - Recent changes 3. **Containers**: `mcp__podman__container_list` - Running state 4. **PM2 Status**: `podman exec flyer-crawler-dev pm2 status` - Process health (API, Worker, Vite) --- ## Quick Reference ### Essential Commands | Command | Description | | ------------------------------------------------------------ | --------------------- | | `podman exec -it flyer-crawler-dev npm test` | Run all tests | | `podman exec -it flyer-crawler-dev npm run test:unit` | Unit tests only | | `podman exec -it flyer-crawler-dev npm run type-check` | TypeScript check | | `podman exec -it flyer-crawler-dev npm run test:integration` | Integration tests | | `podman exec -it flyer-crawler-dev pm2 status` | PM2 process status | | `podman exec -it flyer-crawler-dev pm2 logs` | View all PM2 logs | | `podman exec -it flyer-crawler-dev pm2 restart all` | Restart all processes | ### Key Patterns (with file locations) | Pattern | ADR | Implementation | File | | ------------------ | ------- | ------------------------------------------------- | ----------------------------------- | | Error Handling | ADR-001 | `handleDbError()`, throw `NotFoundError` | `src/services/db/errors.db.ts` | | Repository Methods | ADR-034 | `get*` (throws), `find*` (null), `list*` (array) | `src/services/db/*.db.ts` | | API Responses | ADR-028 | `sendSuccess()`, `sendPaginated()`, `sendError()` | `src/utils/apiResponse.ts` | | Transactions | ADR-002 | `withTransaction(async (client) => {...})` | `src/services/db/transaction.db.ts` | ### Key Files Quick Access | Purpose | File | | ----------------- | -------------------------------- | | Express app | `server.ts` | | Environment | `src/config/env.ts` | | Routes | `src/routes/*.routes.ts` | | Repositories | `src/services/db/*.db.ts` | | Workers | `src/services/workers.server.ts` | | Queues | `src/services/queues.server.ts` | | PM2 Config (Dev) | `ecosystem.dev.config.cjs` | | PM2 Config (Prod) | `ecosystem.config.cjs` | --- ## Application Overview **Flyer Crawler** - AI-powered grocery deal extraction and analysis platform. **Data Flow**: Upload → AI extraction (Gemini) → PostgreSQL → Cache (Redis) → API → React display **Architecture** (ADR-035): ```text Routes → Services → Repositories → Database ↓ External APIs (*.server.ts) ``` **Key Entities**: Flyers, FlyerItems, Stores, StoreLocations, Users, Watchlists, ShoppingLists, Recipes, Achievements **Full Architecture**: See [docs/architecture/OVERVIEW.md](docs/architecture/OVERVIEW.md) --- ## Dev Container Architecture (ADR-014) The dev container now matches production by using PM2 for process management. ### Process Management | Component | Production | Dev Container | | ---------- | ---------------------- | ------------------------- | | API Server | PM2 cluster mode | PM2 fork mode + tsx watch | | Worker | PM2 process | PM2 process + tsx watch | | Frontend | Static files via NGINX | PM2 + Vite dev server | | Logs | PM2 logs -> Logstash | PM2 logs -> Logstash | **PM2 Processes in Dev Container**: - `flyer-crawler-api-dev` - API server (port 3001) - `flyer-crawler-worker-dev` - Background job worker - `flyer-crawler-vite-dev` - Vite frontend dev server (port 5173) ### Log Aggregation (ADR-050) All logs flow to Bugsink via Logstash with 3-project routing: | Source | Log Location | Bugsink Project | | ----------------- | --------------------------------- | ------------------ | | Backend (Pino) | `/var/log/pm2/api-*.log` | Backend API (1) | | Worker (Pino) | `/var/log/pm2/worker-*.log` | Backend API (1) | | PostgreSQL | `/var/log/postgresql/*.log` | Backend API (1) | | Vite | `/var/log/pm2/vite-*.log` | Infrastructure (4) | | Redis | `/var/log/redis/redis-server.log` | Infrastructure (4) | | NGINX | `/var/log/nginx/*.log` | Infrastructure (4) | | Frontend (Sentry) | Browser -> nginx proxy | Frontend (2) | **Bugsink Projects (Dev Container)**: - Project 1: Backend API (Dev) - Application errors - Project 2: Frontend (Dev) - Browser errors via nginx proxy - Project 4: Infrastructure (Dev) - Redis, NGINX, Vite errors **Key Files**: - `ecosystem.dev.config.cjs` - PM2 development configuration - `scripts/dev-entrypoint.sh` - Container startup script - `docker/logstash/bugsink.conf` - Logstash pipeline configuration - `docker/nginx/dev.conf` - NGINX config with Bugsink API proxy **Full Dev Container Guide**: See [docs/development/DEV-CONTAINER.md](docs/development/DEV-CONTAINER.md) --- ## Common Workflows ### Adding a New API Endpoint 1. Add route in `src/routes/{domain}.routes.ts` 2. Use `validateRequest(schema)` middleware for input validation 3. Call service layer (never access DB directly from routes) 4. Return via `sendSuccess()` or `sendPaginated()` 5. Add tests in `*.routes.test.ts` **Example Pattern**: See [docs/development/CODE-PATTERNS.md](docs/development/CODE-PATTERNS.md) ### Adding a New Database Operation 1. Add method to `src/services/db/{domain}.db.ts` 2. Follow naming: `get*` (throws), `find*` (returns null), `list*` (array) 3. Use `handleDbError()` for error handling 4. Accept optional `PoolClient` for transaction support 5. Add unit test ### Adding a Background Job 1. Define queue in `src/services/queues.server.ts` 2. Add worker in `src/services/workers.server.ts` 3. Call `queue.add()` from service layer --- ## Subagent Delegation Guide **When to Delegate**: Complex work, specialized expertise, multi-domain tasks ### Decision Matrix | Task Type | Subagent | Key Docs | | --------------------- | ----------------------- | ----------------------------------------------------------------- | | Write production code | coder | [CODER-GUIDE.md](docs/subagents/CODER-GUIDE.md) | | Database changes | db-dev | [DATABASE-GUIDE.md](docs/subagents/DATABASE-GUIDE.md) | | Create tests | testwriter | [TESTER-GUIDE.md](docs/subagents/TESTER-GUIDE.md) | | Fix failing tests | tester | [TESTER-GUIDE.md](docs/subagents/TESTER-GUIDE.md) | | Container/deployment | devops | [DEVOPS-GUIDE.md](docs/subagents/DEVOPS-GUIDE.md) | | UI components | frontend-specialist | [FRONTEND-GUIDE.md](docs/subagents/FRONTEND-GUIDE.md) | | External APIs | integrations-specialist | [INTEGRATIONS-GUIDE.md](docs/subagents/INTEGRATIONS-GUIDE.md) | | Security review | security-engineer | [SECURITY-DEBUG-GUIDE.md](docs/subagents/SECURITY-DEBUG-GUIDE.md) | | Production errors | log-debug | [SECURITY-DEBUG-GUIDE.md](docs/subagents/SECURITY-DEBUG-GUIDE.md) | | AI/Gemini issues | ai-usage | [AI-USAGE-GUIDE.md](docs/subagents/AI-USAGE-GUIDE.md) | | Planning features | planner | [DOCUMENTATION-GUIDE.md](docs/subagents/DOCUMENTATION-GUIDE.md) | **All Subagents**: See [docs/subagents/OVERVIEW.md](docs/subagents/OVERVIEW.md) **Launch Pattern**: ``` Use Task tool with subagent_type: "coder", "db-dev", "tester", etc. ``` --- ## Known Issues & Gotchas ### Integration Test Issues (Summary) Common issues with solutions: 1. **Vitest globalSetup context isolation** - Mocks/spies don't share instances → Mark `.todo()` or use Redis-based flags 2. **Cleanup queue interference** - Worker processes jobs during tests → `cleanupQueue.drain()` and `.pause()` 3. **Cache staleness** - Direct SQL bypasses cache → `cacheService.invalidateFlyers()` after inserts 4. **Filename collisions** - Multer predictable names → Use `${Date.now()}-${Math.round(Math.random() * 1e9)}` 5. **Response format mismatches** - API format changes → Log response bodies, update assertions 6. **External service failures** - PM2/Redis unavailable → try/catch with graceful degradation **Full Details**: See test issues section at end of this document or [docs/development/TESTING.md](docs/development/TESTING.md) ### Git Bash Path Conversion (Windows) Git Bash auto-converts Unix paths, breaking container commands. **Solutions**: ```bash # Use sh -c with single quotes podman exec container sh -c '/usr/local/bin/script.sh' # Use MSYS_NO_PATHCONV=1 MSYS_NO_PATHCONV=1 podman exec container /path/to/script # Use Windows paths for host files podman cp "d:/path/file" container:/tmp/file ``` --- ## Configuration & Environment ### Environment Variables **See**: [docs/getting-started/ENVIRONMENT.md](docs/getting-started/ENVIRONMENT.md) for complete reference. **Quick Overview**: - **Production**: Gitea CI/CD secrets only (no `.env` file) - **Test**: Gitea secrets + `.env.test` overrides - **Dev**: `.env.local` file (overrides `compose.dev.yml`) **Key Variables**: `DB_HOST`, `DB_USER`, `DB_PASSWORD`, `JWT_SECRET`, `VITE_GOOGLE_GENAI_API_KEY`, `REDIS_URL` **Adding Variables**: Update `src/config/env.ts`, Gitea Secrets, workflows, `ecosystem.config.cjs`, `.env.example` ### MCP Servers **See**: [docs/tools/MCP-CONFIGURATION.md](docs/tools/MCP-CONFIGURATION.md) for setup. **Quick Overview**: | Server | Purpose | Config | | -------------------------- | -------------------- | ---------------------- | | gitea-projectium/torbonium | Gitea API | Global `settings.json` | | podman | Container management | Global `settings.json` | | memory | Knowledge graph | Global `settings.json` | | redis | Cache access | Global `settings.json` | | bugsink | Prod error tracking | Global `settings.json` | | localerrors | Dev Bugsink | Project `.mcp.json` | | devdb | Dev PostgreSQL | Project `.mcp.json` | **Note**: Localhost servers use project `.mcp.json` due to Windows/loader issues. ### Bugsink Error Tracking **See**: [docs/tools/BUGSINK-SETUP.md](docs/tools/BUGSINK-SETUP.md) for setup. **Quick Access**: - **Dev**: https://localhost:8443 (`admin@localhost`/`admin`) - **Prod**: https://bugsink.projectium.com **Token Creation** (required for MCP): ```bash # Dev container MSYS_NO_PATHCONV=1 podman exec -e DATABASE_URL=postgresql://bugsink:bugsink_dev_password@postgres:5432/bugsink -e SECRET_KEY=dev-bugsink-secret-key-minimum-50-characters-for-security flyer-crawler-dev sh -c 'cd /opt/bugsink/conf && DJANGO_SETTINGS_MODULE=bugsink_conf PYTHONPATH=/opt/bugsink/conf:/opt/bugsink/lib/python3.10/site-packages /opt/bugsink/bin/python -m django create_auth_token' # Production (via SSH) ssh root@projectium.com "cd /opt/bugsink && bugsink-manage create_auth_token" ``` ### Logstash **See**: [docs/operations/LOGSTASH-QUICK-REF.md](docs/operations/LOGSTASH-QUICK-REF.md) Log aggregation: PostgreSQL + PM2 + Redis + NGINX → Bugsink (ADR-050) --- ## Documentation Quick Links | Topic | Document | | ------------------- | -------------------------------------------------------------- | | **Getting Started** | [QUICKSTART.md](docs/getting-started/QUICKSTART.md) | | **Dev Container** | [DEV-CONTAINER.md](docs/development/DEV-CONTAINER.md) | | **Architecture** | [OVERVIEW.md](docs/architecture/OVERVIEW.md) | | **Code Patterns** | [CODE-PATTERNS.md](docs/development/CODE-PATTERNS.md) | | **Testing** | [TESTING.md](docs/development/TESTING.md) | | **Debugging** | [DEBUGGING.md](docs/development/DEBUGGING.md) | | **Database** | [DATABASE.md](docs/architecture/DATABASE.md) | | **Deployment** | [DEPLOYMENT.md](docs/operations/DEPLOYMENT.md) | | **Monitoring** | [MONITORING.md](docs/operations/MONITORING.md) | | **Logstash** | [LOGSTASH-QUICK-REF.md](docs/operations/LOGSTASH-QUICK-REF.md) | | **ADRs** | [docs/adr/index.md](docs/adr/index.md) | | **All Docs** | [docs/README.md](docs/README.md) | --- ## Appendix: Integration Test Issues (Full Details) ### 1. Vitest globalSetup Context Isolation Vitest's `globalSetup` runs in separate Node.js context. Singletons, spies, mocks do NOT share instances with test files. **Affected**: BullMQ worker service mocks (AI/DB failure tests) **Solutions**: Mark `.todo()`, create test-only API endpoints, use Redis-based mock flags ```typescript // DOES NOT WORK - different instances const { flyerProcessingService } = await import('../../services/workers.server'); flyerProcessingService._getAiProcessor()._setExtractAndValidateData(mockFn); ``` ### 2. Cleanup Queue Deletes Before Verification Cleanup worker processes jobs in globalSetup context, ignoring test spies. **Solution**: Drain and pause queue: ```typescript const { cleanupQueue } = await import('../../services/queues.server'); await cleanupQueue.drain(); await cleanupQueue.pause(); // ... test ... await cleanupQueue.resume(); ``` ### 3. Cache Stale After Direct SQL Direct `pool.query()` inserts bypass cache invalidation. **Solution**: `await cacheService.invalidateFlyers();` after inserts ### 4. Test Filename Collisions Multer predictable filenames cause race conditions. **Solution**: Use unique suffix: `${Date.now()}-${Math.round(Math.random() * 1e9)}` ### 5. Response Format Mismatches API formats change: `data.jobId` vs `data.job.id`, nested vs flat, string vs number IDs. **Solution**: Log response bodies, update assertions ### 6. External Service Availability PM2/Redis health checks fail when unavailable. **Solution**: try/catch with graceful degradation or mock