This project is organized into 6 phases (P0–P5), each in its own self-contained folder.
| Phase | Name | Status | Folder |
|---|---|---|---|
| P0 | Foundation | ✅ Complete | p0/ |
| P1 | Job Harvester | ✅ Complete | p1/ |
| P2 | Resume Tailor | ✅ Complete | p2/ |
| P3 | The Closer | ✅ Complete | p3/ |
| P4 | Integration | ✅ Complete | p4/ |
| P5 | Polish | ✅ Complete | p5/ |
Each phase is self-contained with its own apps, packages, services, configuration, and phase-specific README. Planning and architecture documents live in /docs/ at the project root. Phases depend on earlier phases (P1 depends on P0, P2 depends on P0, etc.).
Goal: Monorepo scaffolding, database schema, authentication, CI/CD, shared UI components.
| Directory / File | Purpose |
|---|---|
apps/web/ |
Next.js 15 frontend (App Router, auth, dashboard, login, Shadcn UI) |
packages/shared/ |
Shared TypeScript types (Zod schemas), Drizzle ORM schema, DB migrations, auth session helpers |
packages/py-shared/ |
Shared Python models (SQLAlchemy) matching the DB schema |
prompts/ |
LLM prompt templates for resume analysis, tailoring, guardrails, outreach |
scripts/ |
Utility scripts (DB migration runner) |
e2e/ |
Playwright end-to-end tests |
uploads/ |
Uploaded file storage |
.github/ |
CI/CD workflows |
| Root configs | tsconfig.json, vitest.config.ts, docker-compose.yml, .env.example, .prettierrc, pyproject.toml |
# From project root
npm run dev # Start dev servers
npm run build # Build all workspaces
npm run db:migrate # Apply DB migrations
npm run db:seed # Seed demo data
# From phases/p0/
docker compose up # Start PostgreSQL, Redis, and web appComplete. All P0 tasks are implemented:
- Monorepo scaffolding (Next.js, FastAPI, Turborepo, shared packages)
- Database schema & migrations (PostgreSQL, Drizzle ORM, SQLAlchemy)
- Authentication (NextAuth.js with magic link, OAuth, JWT sessions)
- Shared UI components (Shadcn: Button, Card, Badge, Input, Dialog, Tabs, Sonner, Skeleton, ErrorBoundary)
- CI/CD (GitHub Actions, Docker setup, Vitest/pytest config, Vercel deployment config)
Nothing — this is the foundation phase.
Goal: Multi-source job aggregation, filtering, deduplication, and REST API.
Complete. All P1 tasks are implemented:
models.py— Pydantic schemas (JobSearchRequest,JobResponse,SearchStatusResponse) + SQLAlchemyJobModelnormalizer.py— Date parsing (12+ formats), salary extraction (USD, INR, EUR, GBP), location normalization, experience inferencededup.py— Fuzzy matching viathefuzz(token_set_ratio), URL dedup, source+source_id dedupfilter.py— Keyword scoring, location/remote/experience/date filters, sorted by relevancestore.py— Async SQLAlchemy CRUD with upsert (conflict on source+source_id), search, paginationpipeline.py—JobPipelineorchestrator: fetch → normalize → filter → dedup → store (with parallel source fetching)
adapters/base.py— AbstractJobSourceAdapter,SearchParams,RawJobListingdataclassesadapters/remoteok.py— HTTP API client for remoteok.comadapters/naukri.py— Selenium-based scraper for naukri.com (headless, cookie consent, pagination)adapters/wellfound.py— Firecrawl-based extractor with HTTP fallback for wellfound.comadapters/__init__.py— Adapter factory registrydocs/extension-guide.md— Interface contracts for adding new sources
POST /api/jobs/search— async search with pollingGET /api/jobs/search/{search_id}— poll search statusGET /api/jobs/search?q=...— quick sync searchGET /api/jobs/— list jobs with filtering/paginationGET /api/jobs/{id}— get single jobDELETE /api/jobs/{id}— delete jobGET /api/jobs/sources— list available sources with metadataGET /api/jobs/export— CSV export of job listings
exporters/csv.py— CSV generation with proper headersexporters/application.py— Batch application creation from jobsroutes/export.py— Streaming CSV download endpoint
test_normalizer.py— 15+ test cases for date parsing, salary extraction, location normalizationtest_dedup.py— 10+ test cases for exact/fuzzy/edge-case duplicatestest_filter.py— Per-filter tests + integration scenariostest_api.py— Endpoint tests with DB mockingmock_data/— Fixture files for reproducible tests
cd phases/p1/harvester
pip install -r requirements.txt
pip install pytest pytest-asyncio # only needed for testspython -m pytest tests/ -vAll 71 tests should pass.
uvicorn app.main:app --reload --port 8001The API will be available at http://localhost:8001.
# Health check
curl http://localhost:8001/health
# List available sources
curl http://localhost:8001/api/jobs/sources
# Search jobs (runs pipeline against live sources)
curl -X POST http://localhost:8001/api/jobs/search \
-H "Content-Type: application/json" \
-d '{"keywords": ["software engineer"]}'
# List harvested jobs
curl http://localhost:8001/api/jobs/Note: The service connects to PostgreSQL at startup using the DATABASE_URL from .env. If no database is available, the health endpoint still works but API endpoints that query the database will return errors.
# From project root
docker compose up harvester
# Or build and run manually
cd phases/p1/harvester
docker build -t jobplatform-harvester .
docker run -p 8001:8001 jobplatform-harvester- P0 — Database schema and shared Python models
Goal: AI-powered resume analysis, tailoring, guardrails, and PDF export.
Complete. All P2 tasks are implemented across 7 sub-tasks.
packages/shared/lib/resume/parser.ts— PDF/DOCX/TXT parser with section extraction (education, experience, skills, etc.)apps/web/app/api/resume/upload/route.ts— File upload API (multipart, 10 MB limit, type validation)apps/web/app/api/resume/store.ts— In-memory resume storage with CRUD operationsapps/web/components/resume/FileUploader.tsx— Drag-and-drop upload UI with progress bar
packages/shared/lib/llm/client.ts— Groq SDK wrapper with retry (3 attempts, exponential backoff), timeout (30s), circuit breaker (5 failures/60s window)packages/shared/lib/llm/config.ts— Env-based config with Zod validation (GROQ_API_KEY,LLM_MODEL, etc.)packages/shared/lib/llm/prompts.ts— Prompt template loader with variable interpolation and cachingpackages/shared/lib/llm/cache.ts— In-memory cache with TTL, pattern invalidationpackages/shared/lib/llm/mock.ts— Deterministic mock responses for UI dev (auto-enabled whenMOCK_MODE=true)prompts/resume/analyze.txt— Analysis prompt templateprompts/resume/tailor.txt— Tailoring prompt templateprompts/resume/gaps.txt— Gap analysis prompt templateprompts/guardrails/truthfulness.txt— Guardrail verification prompt
packages/shared/lib/services/analyzer.ts— Resume analyzer (score 0-100, skill breakdown, strengths/weaknesses)packages/shared/lib/services/gap-analyzer.ts— Gap analysis with importance ratingsapps/web/app/api/resume/[id]/analyze/route.ts—POST /api/resume/:id/analyzeendpointapps/web/components/resume/ScoreGauge.tsx— Animated radial gauge (red < 40, yellow 40-70, green > 70)apps/web/components/resume/GapList.tsx— Expandable gap list grouped by importance
packages/shared/lib/services/tailor.ts— Per-section bullet rewriting via LLMpackages/shared/lib/services/diff.ts— Line-by-line diff engine (added, removed, modified, unchanged)apps/web/app/api/resume/[id]/tailor/route.ts—POST /api/resume/:id/tailorendpointapps/web/components/resume/TailorButton.tsx— "Generate Tailored Resume" button with ETA displayapps/web/components/resume/DiffViewer.tsx— Side-by-side and unified diff views with color coding
packages/shared/lib/services/guardrails.ts— LLM-based guardrail checker (truthfulness, fabrication, seniority)packages/shared/lib/services/guardrails/truthfulness.ts— Title inflation and metric fabrication detectionpackages/shared/lib/services/guardrails/fabrication.ts— Company name, certification, and degree fabrication detectionpackages/shared/lib/services/guardrails/seniority.ts— Seniority level and years of experience validationapps/web/components/resume/GuardrailBadge.tsx— Pass/Warning/Fail badge with expandable details
packages/shared/lib/pdf/generator.ts— HTML-to-PDF generation with styled outputpackages/shared/lib/pdf/comparison.ts— Comparison report (score, gaps, guardrails, side-by-side)apps/web/app/api/resume/[id]/export/route.ts—POST /api/resume/:id/exportendpointapps/web/components/resume/DownloadButton.tsx— Download buttons for tailored PDF and comparison report
packages/shared/__tests__/resume/parser.test.ts— Section extraction, TXT parsing, edge casespackages/shared/__tests__/llm/client.test.ts— Cache operations, TTL, key hashing, pattern invalidationpackages/shared/__tests__/services/guardrails.test.ts— Title inflation, metric fabrication, company/degree fabrication, seniority/experience inflationpackages/shared/__tests__/services/diff.test.ts— Change detection, added/removed/modified lines, change counting
# Ensure deps are installed
cd phases/p0 && npm install
# Run tests
cd phases/p0 && npx vitest run --config vitest.config.ts
# Start the web app (which includes resume API routes)
cd phases/p0 && npm run dev- groq-sdk — LLM API client (shared package)
- mammoth — DOCX text extraction (web app)
- pdf-parse — PDF text extraction (web app)
- P0 — Database schema, auth, UI components, shared types
Goal: Cold email generation, preview, SMTP delivery, and audit logging.
Complete. All P3 tasks are implemented across 6 sub-tasks.
app/email_generator.py— Jinja2 template-based email generation (cold + follow-up)app/personalizer.py— Personalisation context builder with company insight, resume highlights, intro/body/closing renderersapp/email_generator_llm.py— LLM-powered generation placeholder (post-MVP, delegates to template generator)app/templates/— 4 templates:cold_email.html,cold_email.txt,follow_up.html,follow_up.txt
app/email_sender.py—smtplib-based sender with STARTTLS,Message-ID/In-Reply-Toheaders, dry-run modeapp/rate_limiter.py— Token bucket rate limiter (20/hr, 100/day per user) withRetry-Afterheadersapp/queue.py— In-memory queue with retry (linear backoff 30s→60s→120s), circuit breaker (3 failures → 5min pause)
app/preview.py— HTML email renderer with link extraction and read time estimationcli/main.py— Terminal CLI with preview/send workflows and interactive promptcli/preview.py— Colorized terminal email previewapps/web/components/outreach/EmailPreview.tsx— Iframe HTML preview with toggleable text view, "Looks Good"/"Edit" buttons
app/logger.py— In-memory outreach logger with per-user indexing, stats aggregation (open/reply/bounce rates)app/delivery_tracker.py— Delivery event tracking (delivered, bounced, opened, replied)app/export.py— CSV export of outreach logsroutes/outreach.py— 8 endpoints (generate, preview, send, queue, logs, stats, track, export)
apps/web/components/outreach/SendQueue.tsx— List with status badges, search/filter, send/delete actionsapps/web/components/outreach/DeliveryLog.tsx— Sortable table with recipient, subject, date, status, resendapps/web/components/outreach/StatsCards.tsx— 4 metric cards (total sent, open rate, reply rate, bounce rate)apps/web/components/outreach/VolumeGauge.tsx— Hourly/daily cap progress bars (yellow at 80%, red at 95%)
tests/test_email_generator.py— Template rendering, personalisation, edge cases (7 tests)tests/test_email_sender.py— Rate limiter (hourly/daily/user isolation/reset), dry-run, SMTP failure (8 tests)tests/test_api.py— All 8 endpoints tested with mock data (7 tests)
cd phases/p3/closer
pip install -r requirements.txt
python -m pytest tests/ -v # 22 tests
uvicorn app.main:app --reload --port 8002 # FastAPI on :8002# Preview a cold email
python -m cli.main preview --company "TechCorp" --role "Engineer" --name "Jane"
# Send an email (interactive)
python -m cli.main send --email "jane@techcorp.com" --company "TechCorp"| Method | Path | Description |
|---|---|---|
| POST | /api/outreach/generate |
Generate email draft |
| POST | /api/outreach/:id/preview |
Preview rendered email |
| POST | /api/outreach/:id/send |
Send email (respects rate limits, dry-run) |
| POST | /api/outreach/:id/queue |
Queue email for delayed send |
| POST | /api/outreach/:id/track |
Track delivery event |
| GET | /api/outreach/logs |
Paginated outreach logs |
| GET | /api/outreach/stats |
Aggregated stats + remaining capacity |
| GET | /api/outreach/export |
CSV export of logs |
- P0 — SMTP config, rate limiting defaults, UI components
Goal: Connect all three services into a unified platform with a pipeline orchestrator, unified UI, application tracking, and real-time event bus.
Complete. All P4 tasks are implemented across 5 sub-tasks.
| Directory / File | Purpose |
|---|---|
orchestrator/src/main.ts |
Express entry point (port 8100) |
orchestrator/src/pipeline.ts |
State machine (12 states) + in-process event bus |
orchestrator/src/routes/pipeline.ts |
REST API (start, get, transition, list) |
orchestrator/src/workflows/full-pipeline.ts |
7-step workflow with service integration |
orchestrator/src/state.ts |
Pipeline persistence + cleanup |
orchestrator/src/recovery.ts |
Error recovery with retry logic |
orchestrator/src/analytics.ts |
Event-based analytics consumer |
orchestrator/src/followups.ts |
Follow-up scheduler (3/7/14 day reminders) |
- State machine — 12 states (
idle→searching_jobs→ ... →completed/failed) with valid transition matrix - REST API —
POST /start,GET /:id,POST /:id/transition,GET / - Workflow — 7-step pipeline: search → upload → analyze → tailor → guardrail → generate → send
- State persistence —
persistState(),recoverOnRestart(),cleanupAbandoned()(24h TTL) - Error recovery —
recoverPipeline()with max 3 retries, user-friendly error messages
packages/shared/events/bus.ts— Typed Pub/Sub with in-memory backend (Redis-ready)packages/shared/events/schemas.ts— 11 typed event interfaces (harvester, resume, outreach, pipeline)apps/web/lib/websocket.ts— Client-side WebSocket adapter with subscription managementorchestrator/src/analytics.ts— Listens to all events, aggregates dashboard stats
- Dashboard (
/dashboard/page.tsx) — Stats cards (Jobs Discovered, Applications, Outreach Sent) - Resume Studio (
/dashboard/resume/page.tsx) — Upload zone, quick actions, activity log - Outreach Console (
/dashboard/outreach/page.tsx) — Stats cards, send queue, delivery log - Application Tracker (
/dashboard/tracker/page.tsx) — 9-column Kanban board with metric cards - Tracker components —
KanbanBoard(drag-and-drop),StatusBadge(color-coded),MetricCards
packages/shared/lib/services/applications.ts— CRUD with status transition validation + timeline eventsapps/web/app/api/applications/route.ts— REST API (GET, POST, PATCH, DELETE)packages/shared/lib/services/stats.ts— Dashboard stats aggregationorchestrator/src/followups.ts— Follow-up reminders (3d, 7d, 14d after outreach)
e2e/full-pipeline.spec.ts— 14 test cases covering the full pipelinee2e/multi-user.spec.ts— Multi-user isolation teste2e/concurrent.spec.ts— Concurrent pipeline teste2e/error-recovery.spec.ts— Error recovery test
# Start the orchestrator
cd phases/p4/orchestrator
npm install
npx tsx src/main.ts
# Access the API
curl http://localhost:8100/health
curl -X POST http://localhost:8100/api/pipeline/start
# Run all integration tests
cd phases/p0
npx vitest run --config vitest.config.ts| Method | Path | Description |
|---|---|---|
| GET | /health |
Health check |
| POST | /api/pipeline/start |
Create and start a new pipeline |
| GET | /api/pipeline/ |
List all pipelines |
| GET | /api/pipeline/:id |
Get pipeline context |
| POST | /api/pipeline/:id/transition |
Move pipeline to next state |
- P1 — Job Harvester API (for job search step)
- P2 — Resume Tailor API (for analyze/tailor/guardrail steps)
- P3 — The Closer API (for outreach steps)
Goal: Performance optimization, security hardening, UX refinement, and mobile responsiveness.
Complete. All P5 tasks are implemented across 4 sub-tasks.
- LLM cache warm-up —
packages/shared/lib/llm/warmup.tspre-populates cache with common queries at startup - Bundle optimization —
next.config.mjsconfigured withremoveConsolein production, image format optimization (WebP/AVIF), device sizes, and immutable static asset caching (1 year) - API response caching —
Cache-Control: no-storefor API routes,public, max-age=31536000, immutablefor static assets
- Rate limiting —
apps/web/lib/rate-limit.tstoken bucket middleware (60 req/min per IP, 120 req/min per user) withRetry-Afterheaders - Input sanitization —
packages/shared/lib/security/sanitize.tsstrips HTML tags/event handlers/JS protocol, validates file MIME types + magic bytes - PII masking —
packages/shared/lib/security/pii.tsmasks phone numbers, emails, SSNs, addresses before LLM calls;restorePII()to reverse after processing - CSRF protection —
packages/shared/lib/security/csrf.tsdouble-submit cookie pattern - Security headers — CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, X-XSS-Protection, Permissions-Policy in
next.config.mjs - Dependency audit —
.github/dependabot.ymlwith weekly npm/pip/GitHub Actions updates
- Mobile responsive — All dashboard pages use responsive Tailwind classes (md:grid-cols, responsive padding)
- Empty states —
components/ui/empty-state.tsxreusable component with icon, title, description, CTA button - Loading state —
app/loading.tsxwith centered spinner - Custom 404 page —
app/not-found.tsxwith "Go to Dashboard" link - Custom error page —
app/error.tsxwith retry button and error message display - Onboarding flow —
components/ui/onboarding.tsxstep-by-step guided tour with localStorage dismissal - Keyboard shortcuts —
components/ui/keyboard-shortcuts.tsxwith configurable shortcuts,?help overlay
- Structured logging —
apps/web/lib/logger.tsJSON logger with level, timestamp, service, requestId, userId, duration fields - Health check —
apps/web/lib/health.tsenhanced/healthreturning DB/Redis/LLM dependency status + uptime - Metrics export —
apps/web/lib/metrics.tsPrometheus-compatible request counter + latency histogram - Distributed tracing —
apps/web/lib/tracing.tsOpenTelemetry-compatible trace IDs viax-trace-id/x-span-idheaders with span recording - Alert rules —
docs/AlertRules.mddefines thresholds for LLM, SMTP, application health, infrastructure, and business metrics
packages/shared/__tests__/security/sanitize.test.ts— 9 tests covering PII masking (phone, email, SSN, restore), HTML sanitization, filename sanitization, MIME validation, magic byte validation