•
4 min read
LangHire: Job Applications That Never Invent Facts
Fork of an open-source desktop app (Tauri + Python) by another author. The original app and its CI are theirs; this page covers only my changes.
Python · FastAPI · Playwright · Chrome extension · Local LLM (Ollama) · SQLite · Pytest

LangHire is an open-source desktop app that fills job applications. I forked it for my own job search and changed what I could not accept in a tool that writes to employers in my name. I made 64 commits between late October and early November 2026.

The failure modes

A form filler driven by a language model fails in three ways that matter:

  1. It invents facts. Years of experience, end dates, salaries, degrees.
  2. It reports success that did not happen. The form was filled, but the employer’s system never accepted it.
  3. It gets stuck. A CAPTCHA or a login challenge stalls an unattended run.

What I built

Checkable answers come only from confirmed facts. Every question with a checkable answer (dates, education, documents, numbers) is answered from a fact I confirmed myself, with its source. Numbers and biographies invented by the model are rejected. If a fact is missing, the application stops and asks; it does not guess. Employment end dates are never made up, and the end date is skipped when a role is marked as current.

Eligibility from evidence, not impressions. Work arrangement and location eligibility are decided only from a quote in the posting. When the model points to a quote, the code first checks that the quote really appears in the posting text. Company history and “hybrid or remote” fine print do not count as evidence.

An application counts as sent only when it is verified. A submission is recorded as sent only after the employer’s system or a confirmation e-mail confirms it. An uncertain attempt is never retried blindly. Mailbox replies are classified so I can see which applications pass the first round.

A human where the platform requires one. On platforms with a CAPTCHA, the app fills the form in my own Chrome through a small extension and leaves the CAPTCHA and the final Submit click to me. Waits for a human are time-bounded, so one challenge cannot stall the rest of the queue.

Read-only collection from public sources. Job listings come from the documented public APIs of applicant-tracking systems such as Greenhouse, Lever, Ashby, Workable, Recruitee and Cornerstone. Boards I exclude stay out of discovery, suggestions and the queue.

A local model for open questions. Narrative questions are answered by a model running locally through Ollama, inside a bounded context.

A bug I am glad I caught

The test suite once ran with the launcher’s data-directory variable still exported. It overwrote my real profile and facts with test fixtures. No application was sent, because the queue was empty, and I restored everything from backups. The fix makes the test setup remove that variable before any test runs. I proved it with a decoy directory that the suite must never touch.

What it taught me

Most of the engineering here is about refusing to act: refusing to answer without a fact, to count a submission without proof, or to retry without knowing what happened. That is the same discipline I apply to production integrations.