← Back to the portfolio
Javier Elías Crespo — AI Builder · Solutions & Implementation

What the system says, and what the user sees.

I designed and directed a job-search engine that read 153 employer job boards through their own APIs when I wrote this (about 300 today). Three times it told me something confidently, precisely wrong. Finding out why is the part of the work I like most — so this page is those three, with the numbers.

0employer boards
0listings, Aug 19 run
0seconds end to end
0times it lied to me
Puerto Rico · US citizen · fully bilingual English / Spanish
javierxelias-svg.github.io · javierxelias@gmail.com
01 · The shape

Six thousand listings in, five out.

Aggregators repost the same job with a fresh URL and a rounded-off salary. So the system skips them and reads employers directly — Ashby, Greenhouse and Lever expose their own posting APIs, which return the real range and the real location rules. Cheap filters run first, expensive ones last, and it remembers everything it has already shown me so nothing repeats.

Fetched — 8 sources, 153 employer boards6,162
5,743 of those 6,162 come from employer boards, not job sites.
After the hard rules1,679
Not remote, region-locked, on-call, or below the pay floor. Rules I wrote, applied without mercy.
After deduping against the ledger1,327
Keyed on company and role, never URL — boards repost constantly with new links.
Scored, ranked, worth reading63
Only these get their full page fetched. Fetching is the slow part; don't pay it for a listing that already failed.
Opened and read by a human — me5
The machine narrows. A person decides. The next section is why that step is not optional.
02 · The interesting part

Three times it was confidently wrong.

Every one of these was found the same way — by opening the source instead of trusting my own output. None of them were visible in the data the system had.

CASE 01A salary that was 3× the real one

A role surfaced at nearly a quarter of a million dollars. The number looked wrong for the level, so I opened the posting. It was OTE — on-target earnings, base plus commission. The guaranteed base was in the mid-$70,000s. Nothing in the feed said so; the figure was simply presented as pay.

system said~$237k actually guaranteed~$76k fixevery figure is now base; OTE is labelled OTE
CASE 02Two pay tiers blended into one headline

An employer's own API returns two compensation tiers — one for expensive metros, one for everyone else. Their public board displays a single blended range. A remote candidate outside those metros reads a number they will not be offered. The gap between what the system stores and what the user sees is not a bug in either; it is the seam where support tickets are born.

compensationTiers: [
  { title: "San Francisco, New York",  ~$86k – $109k },
  { title: "United States",            ~$75k – $95k   }   ← what a remote candidate outside those cities is offered
]
compensationTierSummary: "~$75k – $109k"   ← the one on the page
shownone blended range publishedtwo tiers, in their own API fixread the national tier, not the headline
CASE 03Ranked third. Broke both of my hard rules.

A role came back third out of sixty-three at $187,500, marked fully remote and all base. I opened it. Hybrid, three days a week in an office, and paid on commission. Two root causes, and neither was in the module I first blamed: the parser read one "is this remote" field and ignored a contradicting one sitting beside it, and the pay string it was handed had silently dropped the words "On Target Earnings" before it ever arrived. The salary parser was correct the whole time. It was fed a lossy string.

system saidremote · $187,500 base · rank 3 posting said3 days in office · OTE learnedcheck what the function received before rewriting what it does

A correct parser fed a lossy string looks exactly like a broken parser. That one cost me two hours and it is the most useful thing I learned all month.

03 · The rule underneath

It is not allowed to tell me something worked.

The workspace this runs inside forbids the sentence "this should work." Nothing counts as done because it looks done — only when it has run and left a receipt on disk. Every number on this page has a file behind it: the run log, the test output, the raw API response. That constraint is the reason I caught all three of the above instead of shipping a digest that quietly lied to me every morning.

It is also why the system now has 25 test files and why each of the three fixes above is pinned by a regression test, so it cannot come back. Honesty enforced by the machine, not by willpower.

04 · How this was built, plainly

I direct the code. I don't hand-write it.

I use Claude Code every day. I design what the system should do, I run it, I debug it when it breaks, and I verify what it produces — which is how all three errors above were found. I am not going to tell you I hand-wrote thirteen thousand lines of Python, because that is not what happened, and you would find out in the first technical conversation.

What I will claim: I can take something complicated and make it make sense to someone who does not want to learn it. Four years of doing exactly that — two as a butler at The St. Regis, two at Optum explaining health insurance to people who were frightened and confused — and since early 2026, building the technical half with Claude Code. This page is a support ticket I opened against my own system, three times, and closed.