A small applicant-tracking (ATS) app where a recruiter clicks Screen on a candidate and a headless, multi-step AI agent runs in a separate Node worker (not in the request). The agent reads the role rubric, does RAG over a hiring-guidance corpus, calibrates against past shortlisted hires, runs a (deliberately flaky) background check, and produces a structured verdict — recommendation, 0–100 score with a per-criterion breakdown, strengths, gaps, and tailored interview questions. The UI polls job progress and renders a humanized step timeline.
Next.js UI ──POST /api/jobs──▶ jobs table (SQLite) ◀──claims── Node worker
▲ │
└──────────────── polls /api/jobs/:id (steps + result) ◀── writes ───┘
- Next.js (App Router) + TypeScript + Tailwind — UI, API, and job polling.
- Standalone Node worker (
tsx watch worker/index.ts) — claims queued jobs and runs the agent. The API only enqueues; the agent never runs in the request. - Vercel AI SDK v7 + OpenRouter —
generateTexttool-loop +generateObjectverdict. Embeddings via the OpenAI client pointed at OpenRouter. - SQLite + Drizzle ORM (
better-sqlite3, WAL) — one DB file shared by the app and the worker.
All AI/embedding calls are centralized in lib/ai/*.
pnpm install
cp .env.example .env.local # then set OPENROUTER_API_KEY
pnpm db:push # create the SQLite schema
pnpm db:seed # 8 roles, 64 candidates, rubric corpus (embeds if a key is set).env.local:
OPENROUTER_API_KEY=...
OPENROUTER_MODEL=openai/gpt-4o-mini
OPENROUTER_EMBEDDING_MODEL=openai/text-embedding-3-small
DATABASE_URL=file:./talentflow.db
CHAOS_FAILURE_RATE=0.25
pnpm dev # the Next.js app on http://localhost:3000
pnpm worker # the screening agent workerOpen the app, click Screen on a candidate (or Screen all for this role), and watch the step timeline and result fill in. View shows an existing result without re-running the agent.
| script | what it does |
|---|---|
pnpm dev |
Next.js dev server |
pnpm worker |
headless screening worker |
pnpm db:push |
apply the Drizzle schema to SQLite |
pnpm db:seed |
seed roles, candidates, and the rubric corpus |
pnpm db:embed |
backfill rubric-chunk embeddings |
pnpm build / pnpm start |
production build / serve |
This project ships with no telemetry or error-monitoring instrumentation — it
is intentionally a clean baseline. jobs.metadata is a reserved JSON column for
trace-propagation headers if distributed tracing is added later.