A headless job-search scout: it gathers postings from several sources, scores each for fit with a staged gate system, ranks by similarity to roles I already loved, and sends one Telegram digest a day.
Job hunting has a volume problem. Boards and alerts pour out hundreds of postings, almost all of them wrong, and the few good ones get buried. I did not want to read that firehose every morning. I wanted a small piece of software to read it for me, throw out everything that fails my non-negotiables, and hand me a short list with a sentence on why each one is worth a look.
So the pipeline runs on the Mac Mini at 5am, before I am awake, and the only thing I see is the result.
How it works
Five stages, one per morning, no human in the loop until the last line.
Gather. Pull candidate postings from email job alerts, ATS job boards, and a mobile job site, then bundle and de-duplicate by URL into one list.
Score. This is the heart of it. Each posting runs through a staged gate system with early-return logic, so the cheap disqualifying checks happen first and a bad fit never burns tokens on deep analysis. Mission fit comes first, then whether the role is genuinely AI-native, then location and compensation. If a gate kills the role, the pipeline records the reason and stops. Only the survivors get scored on the two dimensions that actually matter: how well the role maps to my story, and how fresh the posting is.
Vibe. Alongside the gates runs a quieter signal. I tagged a handful of roles that felt exactly right, embedded them, and every new posting gets scored by its cosine similarity to the nearest one. Not the average of them, the nearest. The exemplars are deliberately varied, so averaging them into a single centroid blurs the thing that makes each one good. “Closest to any role I loved” is a sharper question than “close to the middle of all of them.”
Save. The scored output lands in the repo as structured JSON, plus a clean Markdown snapshot of each top pick, so every recommendation is auditable after the fact.
Notify. One message a day to a Telegram topic. Top picks grouped by company, each with a one-line reason and a link to the saved posting, then a short “worth a look” list of strong-fit roles that got flagged on logistics like location. If nothing strong came through, it still sends a note, so silence always means the cron broke, never that there was nothing good.
The decisions that mattered
The interesting work was not the gates, it was the judgment calls around them.
Freshness should not gate fit. An early version demoted a perfect role just because the posting was a few weeks old. Wrong. A role that matches my story is a top pick whether it went up today or last month. Freshness moved to being an ordering and annotation signal, never a reason to bury something. Postings with no visible date stopped being penalized at all, because “I could not find a date” is not the same as “this is stale.”
A logistics flag is not a fitness problem. A great role in the wrong city should still tell me it is a great role. Roles flagged on location now get fully scored for fit anyway, so the daily digest can say “this is a strong match, but it is in Zürich” instead of silently dropping it.
Clustering told me how many cover letters to write. Before writing templates, I embedded the whole backlog of roles I had engaged with and clustered them. The data was blunt: almost everything I chase is one shape, with a small second cluster off to the side. Two starter templates cover the field, not the five I assumed.
Why it is built this way
It is headless and it runs on a schedule, like the rest of the studio. Scoring goes through a local model proxy so the model behind “fast and cheap” can change without touching the pipeline. The annotation step that computes similarity is kept out of the scoring hot path, so the part that runs every posting stays light. And the whole thing reports through Telegram, because the only interface I want for this is a single message that arrives while I am still asleep.
It is opinionated to one person and one search. The architecture is not.