Skip to content

job-pilot

One email a day about new jobs that fit you — with ready-to-send cover letters attached.

Every morning, job-scout publishes a public parquet snapshot of open jobs at ~95 technology companies. job-pilot runs right after that publish. It finds the jobs that are new since its last run and match your target roles, sends only those to the deployed job-matcher API for scoring, renders cover-letter PDFs on your personal letterhead for the good matches, and emails you one digest.

The daily flow

flowchart LR
    A[public trends parquet] --> B[find new jobs<br/>DuckDB anti-join]
    B --> C[filter by your<br/>target roles]
    C --> D[score via<br/>job-matcher API]
    D --> E[cover-letter PDFs<br/>on your letterhead]
    E --> F[one digest email]
    C -- no candidates --> F

A quiet day still sends a short email. Silence always means the pipeline is broken, never that there was nothing.

Three ideas the design stands on

  • Stateless. There is no database. DuckDB (in memory) compares two public parquet URLs — today's file against the dated tag of the last successful run. Each job is analyzed exactly once by construction.
  • No LLM inside. All model calls happen in the deployed job-matcher API. job-pilot itself is deterministic code, so its tests are plain pytest — 42 of them, no network, no secrets.
  • Bounded cost. Paid calls need RUN_PAID_MATCH=1 (its absence hard-aborts the run), and matching is skipped for the run — never aborted — if the day's delta exceeds max_jobs_per_run (25); the digest still sends and reports it.

It is a LangGraph pipeline. The graph is not decoration: version 2 adds human-in-the-loop approval for outreach messages, which plugs into LangGraph's checkpointer without a rewrite (see ADR 0003).

Next: Getting Started