12 KiB
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) |
# 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
- Memory:
mcp__memory__read_graph- Recall project context, credentials, known issues - Git:
git log --oneline -10- Recent changes - Containers:
mcp__podman__container_list- Running state
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 |
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 |
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):
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
Common Workflows
Adding a New API Endpoint
- Add route in
src/routes/{domain}.routes.ts - Use
validateRequest(schema)middleware for input validation - Call service layer (never access DB directly from routes)
- Return via
sendSuccess()orsendPaginated() - Add tests in
*.routes.test.ts
Example Pattern: See docs/development/CODE-PATTERNS.md
Adding a New Database Operation
- Add method to
src/services/db/{domain}.db.ts - Follow naming:
get*(throws),find*(returns null),list*(array) - Use
handleDbError()for error handling - Accept optional
PoolClientfor transaction support - Add unit test
Adding a Background Job
- Define queue in
src/services/queues.server.ts - Add worker in
src/services/workers.server.ts - 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 |
| Database changes | db-dev | DATABASE-GUIDE.md |
| Create tests | testwriter | TESTER-GUIDE.md |
| Fix failing tests | tester | TESTER-GUIDE.md |
| Container/deployment | devops | DEVOPS-GUIDE.md |
| UI components | frontend-specialist | FRONTEND-GUIDE.md |
| External APIs | integrations-specialist | INTEGRATIONS-GUIDE.md |
| Security review | security-engineer | SECURITY-DEBUG-GUIDE.md |
| Production errors | log-debug | SECURITY-DEBUG-GUIDE.md |
| AI/Gemini issues | ai-usage | AI-USAGE-GUIDE.md |
| Planning features | planner | DOCUMENTATION-GUIDE.md |
All Subagents: See 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:
- Vitest globalSetup context isolation - Mocks/spies don't share instances → Mark
.todo()or use Redis-based flags - Cleanup queue interference - Worker processes jobs during tests →
cleanupQueue.drain()and.pause() - Cache staleness - Direct SQL bypasses cache →
cacheService.invalidateFlyers()after inserts - Filename collisions - Multer predictable names → Use
${Date.now()}-${Math.round(Math.random() * 1e9)} - Response format mismatches - API format changes → Log response bodies, update assertions
- 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
Git Bash Path Conversion (Windows)
Git Bash auto-converts Unix paths, breaking container commands.
Solutions:
# 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 for complete reference.
Quick Overview:
- Production: Gitea CI/CD secrets only (no
.envfile) - Test: Gitea secrets +
.env.testoverrides - Dev:
.env.localfile (overridescompose.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 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 for setup.
Quick Access:
- Dev: https://localhost:8443 (
admin@localhost/admin) - Prod: https://bugsink.projectium.com
Token Creation (required for MCP):
# 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
Log aggregation: PostgreSQL + PM2 + Redis + NGINX → Bugsink (ADR-050)
Documentation Quick Links
| Topic | Document |
|---|---|
| Getting Started | QUICKSTART.md |
| Architecture | OVERVIEW.md |
| Code Patterns | CODE-PATTERNS.md |
| Testing | TESTING.md |
| Debugging | DEBUGGING.md |
| Database | DATABASE.md |
| Deployment | DEPLOYMENT.md |
| Monitoring | MONITORING.md |
| ADRs | docs/adr/index.md |
| All Docs | 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
// 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:
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