JobLens, every job board in one ranked feed you can check
Job seekers check five to ten boards and a pile of alert emails, and the match scores they see come with no reasons. I specced JobLens and directed AI coding agents to ship it as a public job platform. It ranks 320K+ open jobs from 13 live sources, and every point of the fit score traces to a resume line.
- Score every option before building, and write it down when you overrule the score. The one override in JobLens is the decision I can defend best, because the reason is on record.
- A match score earns trust when each point traces to evidence. A requirement either cites a resume line or counts as a gap, and a missing must-have caps the score.
- Audit every agent run against its own evidence. The build only moved forward once each run re-ran the previous run's checks instead of trusting its summary.
Job seekers check five to ten job boards plus a pile of alert emails. The same role shows up three times, half of it is weeks old, and the “match scores” are numbers nobody explains. Time goes into triage instead of applying early to roles you can actually win.
JobLens is my answer to that. It syncs jobs from public job boards, company ATS career pages (Greenhouse, Lever, Ashby, Workday and others) and the user’s own job-alert emails into one deduplicated feed. Upload a resume once, and the feed is ranked by evidence-backed fit, freshness and competition, with the resume line behind every point of the score.
Live at joblens.club.
The problem#
- Too many sources, all overlapping. The same posting appears on a company careers page, two aggregators and an alert email, each with slightly different text.
- Freshness is hidden. Boards keep evergreen postings open for months, so a feed sorted by relevance fills up with roles that were filled long ago.
- Scores are not explained. A “78% match” with no reasons gives the candidate nothing to act on and no reason to trust it.
- Per-user sync looked impossible to do honestly. The brief asked for sign-in to every platform. The obvious way, logging in to LinkedIn or Naukri from a server with the user’s password, breaks the platforms’ terms and makes a public app hold other people’s credentials.
What I decided#
I wrote the brief, then had the agents run five spikes before any product code: source reachability across 24 job sources, embeddings on the VM’s ARM CPU, resume parsing on ten fictional resumes, the proxy limits of the free hosting, and alert-email parsing. Every architecture choice became a decision record with each option scored 0 to 10 on user value, $0 feasibility, legal and privacy risk, build effort and differentiation, weighted to 100. Fourteen records in all.
- Per-user sync without holding anyone’s password. Server-side login with user credentials scored 43.0 out of 100. The user-side design scored 81.5: Gmail job alerts read with a read-only grant, a browser extension that sends the job cards visible in the user’s own signed-in tab, and paste-a-link. Everyone also gets the public index as a baseline.
- Three-stage ranking. Letting an LLM score every job scored 66.5, because free quotas would run out on day one. The chosen design (86.5) filters and retrieves with BM25 plus local embeddings, computes a deterministic 0 to 100 score, and spends the LLM only on a deep check of the top 10.
- One override, on the record. LinkedIn’s public guest search scored below the pure ATS-and-API mix (70.5 against 81.0) on legal risk. I kept it anyway, because the brief named LinkedIn listings, and capped it hard: on demand only, one request every 4 seconds, 400 a day, a 6-hour stop on any block, and a kill switch.
- Freshness measured honestly. Only 37.6% of all open jobs were posted in the last 30 days. Using “last updated” dates would have made more jobs look fresh (69.5). I chose to serve a 45-day index, where 78.6% are under 30 days old, and to report both numbers every time (90.0).
- $0 hosting with a stable URL. One free-tier VM runs the app and its SQLite index. A Cloudflare quick tunnel exposes it, and a Vercel edge rewrite gives it a fixed public address. The cost is a 120-second request cap and no streaming, so every long job runs in the background and the page polls.
JD Fit, now inside JobLens#
JD Fit started as a standalone engine with its own demo. Rather than keep two tools that score a resume against a job description, I merged it into JobLens and retired the separate app. The old /jdfit link now opens the check page in JobLens.
- A wire map from requirement to evidence. The job description is split into atomic requirements, and each one is wired to the resume line that proves it. A requirement with no line is a gap, not partial credit.
- Must-haves cap the score. Miss one must-have and the score is capped at 69. Miss two and it is capped at 49, however strong the rest of the resume is.
- Tailored bullets with a fabrication guard. JobLens rewrites the top resume bullets for the role. A guard rejects any rewrite that adds a number the resume never had: 0 of 20 tailored bullets carried an invented number in the eval run.
- Honest gap talking points. Each gap gets a short, truthful way to talk about it in an interview (21 points for 21 gaps in the eval run), instead of a suggestion to claim the skill.
- Check any JD. Any job in the ranked feed can be deep-checked, and joblens.club/check takes any pasted job description or public link.
Free to try, with a budget#
JobLens runs on free developer APIs, so the AI parts are rationed and the rest is not.
- Guests get 3 AI checks a day. No account is needed to try it. Signing in with Google or GitHub makes AI checks unlimited.
- A site-wide AI budget, with a fallback. All traffic shares one daily AI budget. When it runs out, or every free model is cooling down, the card says so and shows the non-AI match score instead. Nothing errors.
How it was validated#
AI coding agents wrote the code in hourly autonomous runs against a phased plan. Each run had to re-run the previous run’s evidence before building anything new. That rule paid for itself early: run 3 found that run 2’s “decisions complete” claim did not hold (the option analyses had been deleted and three spikes never ran), and both phases were reopened and redone.
- Retrieval: 70 known-item pairs across 16 industries and 13 countries, each a fictional resume written for a real indexed job, split into a tuning half and a held-out half. Plus 85 hard negatives that must never reach the top 20.
- Deep check: 10 labelled job descriptions against a fictional resume. A requirement gets credit only if the model cites a resume line that exists and shares a meaningful term with the requirement. Otherwise the credit is revoked and the item becomes a gap.
- Dedupe: 100 labelled pairs, then a spot check of 50 real merged jobs in production. The spot check found 6 false merges that the labelled set had missed. Merge vetoes and a re-split took it to 0 of 50.
- The product end to end: a scripted browser walkthrough of the whole flow on the public URL, from upload to delete-my-data, at desktop and phone widths.
Results#
- 320K+ open jobs from 13 live sources in 196 countries, 153K of them in the served 45-day index (Oct 30, 2026).
- Recall@10 0.871 on 70 known-item pairs (0.886 on the held-out half), up from 0.671 for a BM25-only baseline. MRR 0.666.
- 0 hard-constraint violations in the top 20 over 70 queries, and 0 of 85 hard negatives leaked.
- 10/10 fit bands correct on the deep check, with 0 credit given without evidence and 4 of 4 missing must-haves caught.
- Feed p95 719 ms over the full index. First feed in under 8 seconds from upload through the public URL.
- 0 console errors in the public walkthrough at 1440 px and 375 px, and 2 days from the written brief to a live public URL.
Redesign#
The first JobLens was cream paper with one orange accent. Orange marked the main button, a fresh job and every link, so it stopped meaning anything, and the look sat close to the default that many AI-built sites land on.
Before any visual work, I had the agents read how eight job tools present a match: Teal, Jobright, Simplify, Huntr, Jobscan, Welcome to the Jungle, Wellfound and LinkedIn Jobs. That became 12 design principles, and three of them drove most of the redesign:
- Show the working, not the number. A score sits next to the resume line that earned it, on a deep-checked feed card as well as in the report.
- Band first, number second, cap always. The report reads “Low fit, capped at 49” before it shows the number, and the cap is drawn on the same gauge as the score. A flattering number can’t hide a missing must-have.
- Value before an account. The first screen of the landing page holds the upload button, a sample resume and a worked example labelled as fictional.

The agents drafted seven candidate directions, from a highlighted printout to a metro map. The one that shipped is an optician’s eye chart. JobLens claims that every point of a score can be traced to a line you wrote, and an eye chart exists to prove you can read the fine print.
- Colour only for verdicts. Green, ochre and red mean strong, partial and gap, borrowed from the red-green test at the optician, and nothing else uses them. Yellow means a job posted in the last 48 hours. Each verdict also has a shape and a word, because red and green is the hardest pair for colour-blind readers.
- Two typefaces with a job each. Optician Sans, drawn from old eye charts, sets headlines and scores. Everything you read closely is in Atkinson Hyperlegible, which the Braille Institute designed for low-vision readers.
- One gauge everywhere. A semicircle from 0 to 100 with the must-have cap marked on it looks the same on a feed card, a job page and the report.
- Two names for two numbers. The quick first-pass number is “match” and the cited deep check is “fit”. They never share a label or a colour. Simplify shows two scores with similar names on one screen, and its help pages have to explain the difference.
- Limits you can see. The top bar counts free deep checks left today from the first visit, so the third check isn’t a surprise wall.

The JD Fit report draws a wire from each requirement in the job description to the resume line that backs it, solid for strong and dashed for partial. A gap gets no wire and a red tile. On phones the wires fold into a list, with the cited line printed under each requirement.


The redesign is live on every page of the app, from the landing and feed through the report, Check any JD, the tracker and the error pages. Every screenshot here is from the live app with the fictional demo resume.
What is still open#
- Sign-in is partly live. Google and GitHub sign-in work end to end; LinkedIn sign-in is off. Gmail alert sync runs in Google’s testing mode, which limits it to 100 test users.
- Competition is a placeholder. The score has a competition component, but no source exposes applicant counts yet, so it is neutral for every job.
- Three keyed sources are off. Adzuna, Jooble and USAJobs switch on when their free keys exist.
- Small eval sets. 35 held-out pairs put Recall@10 somewhere between roughly 0.74 and 0.96. The deep check is 10 jobs against one resume, which shows it separates clear fits from clear non-fits, not that it is calibrated across industries.
- One VM behind a quick tunnel. A tunnel restart changes the address, and the app is unreachable until a timer redeploys the edge rewrite, between 31 and 125 seconds in testing.
- What I’d do differently: I would make “re-run the previous run’s evidence” the first rule of the build from run 1, not from run 3. A summary written by an agent is a claim, not evidence.
- FastAPI with Jinja and HTMX
- SQLite WAL + FTS5
- BM25 + bge-small embeddings fused by RRF
- JD Fit deep check and /check on free-tier LLMs
- Chrome MV3 extension + Gmail alert parsing
- One free-tier VM behind a Vercel edge rewrite