Pitchtrue

Building an AI career toolkit end to end.

I am building the frontend, backend, job search, and AI tailoring workflow as a solo product.

My role
Solo full-stack engineer
Period
2025 — present
Project type
Independent product · in development

What I built

  1. The application workspace

    Built the search, saved-jobs, tailoring, and tracking interfaces in Next.js, React, and TypeScript.

  2. Search and matching

    Built job ingestion from multiple sources and a search system that ranks roles against the selected résumé.

  3. AI tailoring and quality checks

    Built the AI rewrite pipeline with locked résumé facts, live progress updates, and automated checks for output quality.

Project outcome

Job-card scoring runs without an LLM call. The tailoring pipeline is checked by 1,300+ assertions across 17 résumé and job-description pairs.

Try the demo
Interface recreation with sample jobs and résumé data.

Show why a role is a match

The selected résumé stays visible above the results, and each job card shows its matching skills alongside the score. This keeps the ranking grounded in details people can inspect before opening a role.

Interface preview

Pitchtrue

Pitchtrue interface recreation with job filters, ranked results, and matching scores

Interface recreation with sample jobs and résumé data.

Project context

Job seekers need to find relevant roles and tailor applications without changing the facts in their résumé.

Try the demo
How I built it

Project details

Type
Personal project
Domain
AI résumé platform
Role
Solo · full-stack
Status
In development

Tools & technologies

Next.js 16 · React 19 · TypeScript · Node.js · Claude API · GPT-4 · PostgreSQL / pgvector · Hugging Face · TanStack Query · TanStack Router · AWS · Docker

Project facts

Per cold search
~$0.13 — Down from $1.33. Query-embedding similarity for cross-query reuse + judge-gated cache + actor selection.
Per tailor
~$0.46 — Five-stage pipeline routed per-provider. Postgres-backed phase cache makes re-tailoring essentially free.
Eval harness
1,300+ — Assertions across 17 paired résumé + JD samples spanning engineering, devops, data, AI, finance, healthcare, legal.
Protected résumé facts
Locked — Entity-locking preprocessing pins dates, titles, employer names and numbers before the writer model ever sees them.

Interface notes

Job search

  • Hybrid retrieval does the ranking

    pgvector semantic search, full-text search, and pg_trgm fuzzy matching on PostgreSQL combine into one match score per posting.

  • Skill overlap, made literal

    Every card shows which of the job’s skills the resume already covers — no guessing why a job ranked where it did.

Saved jobs

  • Entity Lock keeps it honest

    Dates, employer names, and verified numbers are pinned before the model sees them — it can rephrase, but it can’t invent.

  • Score lift on every row

    Each saved role carries the value tailoring added, so triage decisions are grounded in a number.

Tailoring

  • A multi-stage analysis pipeline

    Node.js and TypeScript route the rewrite through Claude and GPT-4 across distinct analysis stages.

  • Progress streams as it runs

    Real-time SSE surfaces each stage of the analysis live, instead of a spinner that hides the work.

Tracker

  • The loop closes here

    Outcomes feed back into discovery — every tracked application sharpens the next round of matching.

  • Regression-tested every change

    A Vitest eval harness with 1,300+ assertions runs every change against 17 paired resume-and-JD samples.

Tailoring, without the fabrication

AI-generated résumé text can introduce unsupported skills, metrics, or phrasing. The product needs to preserve the applicant's verified history while changing how it is presented for a role.

Pitchtrue is built on the opposite premise: tailoring means reframing what's already true about you for a specific posting — not inventing a person you aren't. That premise has to be enforced architecturally, not asked for politely in a prompt.

The architecture has three load-bearing pieces. A two-pipeline split puts heuristic scoring on the fast user-facing path ($0 per job card, runs in milliseconds) and reserves LLM cost for deliberate tailoring (~$0.46, gated by explicit user intent). An entity-locking preprocessing layer pins dates, employer names, role titles, and verified numbers as immutable scaffold the writer model never sees as mutable text. A 1,300+ assertion Vitest eval harness gates every pipeline change in CI against seventeen paired résumé-and-JD samples spanning engineering, devops, data, AI, finance, healthcare, and legal — so “improving” a prompt can't silently degrade output quality on résumé types I don't hand-test.

Tailoring means reframing what's already true about you for a specific job — not inventing a person you aren't.— The constraint the whole product is built around

Cost-control, reliability, and routing — by design

The hard problems in an AI résumé product aren't writing prompts. They're keeping LLM cost bounded as usage scales, preventing hallucination structurally (not by asking nicely), and routing each phase to the model that actually fits its workload. Three architectural choices that did the load-bearing.

Two pipelines: $0 scoring, $0.46 tailoring

Running an LLM call for every job card would add cost and latency to the search experience. I separated matching from writing: a heuristic scoring pipeline runs without an LLM charge on every job card (parses the posting, computes coverage % and evidence scores against the chosen résumé, returns 0–100 in milliseconds with no LLM call). The ~$0.46 LLM tailoring pipeline only runs when the user explicitly clicks “Tailor for this job”.

Tradeoff: Two codebases and two quality bars to maintain. In exchange, the job board UX stays fast and free, and LLM cost is bounded by deliberate user intent.

Entity-locking as scaffold, not prompt

Telling a model “don't change the job title” via prompt is unreliable at production scale and silent when it fails. Pitchtrue pins dates, employer names, role titles, and verified numbers in a preprocessing pass as immutable struct fields that the renderer composes into the final document — the writer model never sees them as mutable text. The writer can rephrase a bullet's tense or emphasis freely; it cannot rewrite “Senior Software Engineer” to “Senior Software Architect” because that string isn't part of what it generates.

Tradeoff: Extra preprocessing and a stricter renderer contract. In exchange, the writer cannot modify the protected fields; generated prose is still checked by the evaluation harness.

Per-phase provider routing across model tiers

Different phases of the pipeline have different workloads. Anthropic's Sonnet-tier model writes the user-visible bullets and summary, where quality matters most. Haiku-tier handles scoring and critique, where structured judgment at low latency wins. OpenAI's GPT-4.1-mini handles cover letter generation, where format-following on long-form output is the workload. Cost is tracked per phase per provider so routing can be re-evaluated as model pricing changes.

Tradeoff: Three model integrations, three retry policies, three sets of model-version pinning. In exchange, each phase runs on the model that fits its workload, and per-tailor cost lands ~$0.46 instead of multiples higher.

Entire product, end to end

Frontend in Next.js 16 + React 19 with TanStack Router for typed routes and TanStack Query for server state. Backend in Node.js + Express with Prisma against a PostgreSQL (Neon) instance carrying both pgvector and tsvector extensions. The cost-control architecture is the part I'm most proud of: a heuristic scoring pipeline at $0 per job card, a $0.46 tailoring pipeline gated by explicit user intent, a Postgres-backed per-phase response cache that makes re-tailoring against the same posting essentially free, and a smart-skip + stale-while-revalidate layer that brought per-cold-search cost from $1.33 to $0.06–0.13.

Job ingestion fans out across eleven sources — direct ATS APIs (Greenhouse, Ashby, Lever, Workable), aggregators (Adzuna, Remotive), and external scrapers for LinkedIn, Indeed and Glassdoor. Hybrid retrieval on PostgreSQL combines BM25-style full-text via tsvector, pgvector cosine similarity on locally-generated 384-dim sentence embeddings, and pg_trgm fuzzy matching for typo tolerance — unioned, deduplicated by title+company hash, and rank-normalized per-pool before merging. Real-time SSE streaming surfaces multi-stage tailor progress phase-by-phase as it runs.

Every pipeline change runs against a Vitest eval harness — 1,300+ assertions across seventeen paired résumé-and-JD samples — and CI fails on regression. Final output renders through a Typst-based DOCX pipeline. Deployed on AWS with Docker and GitHub Actions.

Scope

  • Next.js 16 + React 19 frontend with TanStack Router (typed routes) and TanStack Query (server state)
  • Node.js + Express backend, Prisma ORM, PostgreSQL on Neon
  • Hybrid retrieval on PostgreSQL: pgvector (semantic) + tsvector (full-text) + pg_trgm (fuzzy)
  • Locally-hosted sentence-transformer for 384-dim embeddings — résumé data never leaves the server
  • Two LLM pipelines: $0 heuristic ATS scoring and ~$0.46 multi-stage tailoring routed per-provider
  • Entity-locking preprocessing + Vitest eval harness (1,300+ assertions, regression-gated in CI)
  • Job ingestion across 11+ sources: direct ATS APIs (Greenhouse, Ashby, Lever, Workable), aggregators, external scrapers
  • Cost-control: smart-skip, stale-while-revalidate, Postgres-backed LLM phase cache
  • Real-time SSE streaming for phase-by-phase tailor progress
  • Typst-based DOCX renderer for final downloadable output
  • AWS deployment with Docker and GitHub Actions CI/CD