← Dev log
Job Scraper 02·engineering·2026-09-06

A scorer that quietly scored everything zero

A regex that never matched meant keyword relevance scoring returned a flat 0 for every result, silently, with no error.

It said it worked

Green, 200, saved, “already handled” — and wrong. No error anywhere to go on.

Lite Mode is the reason this tool exists: it runs with no API key at all, falling back to keyword matching so someone can try it without signing up for a paid AI provider. It had been scoring every single job a flat 0 since it first shipped.

The cause was a regex that never matched what it was supposed to — so no keyword from the CV or the search ever counted as present in a listing, so every relevance score came out zero. Nothing threw. No log line said anything was wrong. The dashboard rendered a full list of jobs, all scored 0, sorted (badly, see below) and shown. From the outside it looked like Lite Mode worked and just wasn't finding good matches.

This is the failure mode I keep running into and keep writing down: a feature that is present — the code path runs, the UI shows a result, nothing errors — and is broken, and the only way to catch it is to look at the actual output and ask whether the number means anything. A score of 0 for a job that obviously matches the CV is the tell. A test that only checks "the endpoint returns 200 with a list" never sees it.

The fix came with regression tests for the four cases that matter: a clear match, a clear non-match, a word-boundary case (so "java" doesn't match "javascript"), and the score cap. And it landed in the same release that rewrote CI to run the real installed console script against live containers and assert a genuinely scored job comes back — because "the tests pass" had just been proven to mean nothing here.

Two smaller bugs surfaced in the same pass, both the same shape. Notifications were dead by default because the alert threshold was set to 70 on a scale that only goes to 10. And results weren't actually sorted by score, despite the README stating that high scorers bubble to the top. Every one of these is invisible unless you read the values instead of the status code.