three layers · gated at 0.95 macro-F1 · 0.979 measured
Your inbox already holds the verdict.Applied extracts it.
Every application ends as an email — applied, interview, assessment, offer, rejection. The outcome is already written down. A three-layer classifier reads it at the source, so the tracker fills itself.
zero servers · run the classifier ↗
01 · the problem
A job search lives in your inbox.
Dozens of applications. Replies, rejections, assessments, an offer if you are lucky — all arriving unlabeled, interleaved with newsletters and receipts, across two accounts, faster than anyone keeps up with by hand.
- Update on your application to Northstarno-reply@greenhouse.iounclassified
- We received your applicationjobs@stripe.comunclassified
- Take-home exercise — due in 48 hourstalent@harbor.iounclassified
- 12 new roles match your searchdigest@jobboard.comunclassified
- Next steps for your candidacyrecruiting@datadog.comunclassified
- Quick question about your backgroundmaya@earlystage.xyzunclassified
- We’d like to make you an offerpeople@beacon.iounclassifiedoffer
manual tracking is lossy
- The assessment scrolls past — its 48-hour deadline passes unseen.
- “Did I hear back from them?” has no answer but a scroll.
- The row still says applied three weeks after they said no.
- Two accounts, one search — half the thread lives where you aren’t looking.
A manual tracker is only ever as fresh as your discipline. Classification never gets tired.
02 · the shift
Stop tracking. Start classifying.
Tracking is bookkeeping — a symptom. The real task is classification: given an email, which job-search outcome is this, and how sure are we? Solve that at the source and the tracker maintains itself — applications link, statuses advance, and the pipeline is just a projection over labeled mail.
“If you can label the email, you never have to track the application.”
The same seven emails — nothing added, the signal just made legible. The one it is not sure about, it refuses to guess on.
See it label a full sample inbox — real verdicts, no sign-in03 · how it works
Three layers in series, cheapest first.
One email enters the top. Each layer tries to decide; clear its threshold and the cascade files and stops. Fall short and it drops to a smarter, costlier layer — and finally to a gate that would rather ask a human than guess.
Regex rules
201 patterns · 14 ATS domains
Instant, deterministic, fully auditable — the phrases a hiring pipeline actually uses.
accept ≥ 0.90e5 similarity
pretrained e5-small-v2 · 384-d · cosine 1-NN
No rule was sure. Embed the email, match the nearest labeled example — past the exact wording.
accept ≥ 0.85SetFit head
fine-tuned · MiniLM-L6 body · few-shot
The only trained model in the stack — the learned call for the genuinely ambiguous email.
accept ≥ 0.70The 0.85 confidence gate
≥ 0.85 clears the gate → filed under its category, automatically.
< 0.85 a human decides — and the correction becomes new training data.
And the learned model runs in your browser.
The one trained model — the SetFit head — exports to int8 ONNX. 90.4 MB of float32 weights quantize to 22.8 MB; Transformers.js executes them in WebAssembly, on your own CPU. No inference server. No per-request cost. No data leaves the tab.
- model
- 22.8 MB int8 ONNX
- from
- 90.4 MB float32 · ≈4×
- runtime
- Transformers.js · WASM
- privacy
- allowRemoteModels = false
Every verdict, traced.
The signature view: each email’s path through the three layers and the gate. The layer that fired lights in its own hue; earlier layers passed it on, later ones were never needed. Click any row to open its adjudication.
Fired at layer 3 · SetFit few-shot head — the learned call
0%gate 0.85100%Confidence clears the 0.85 gate, so Applied files this as “interview” automatically.
live logic · fixture data — no inbox is read.
04 · the receipts
0.979 macro-F1 — and a gate that blocks merges.
Macro-F1 averages the per-class F1 so no category hides behind the frequent ones. It is not a one-time screenshot: two GitHub Actions gates re-run the evaluation on every backend change and fail the build below 0.95. The number is load-bearing.
0.979
macro-F1 · hybrid classifier v3
accuracy 0.979 · two of the held-out emails misclassified
0.95
macro-F1 CI floor — the merge fails below it
182
tests in the backend suite
9
email classes — 8 predicted + needs_review
6 / 6
output-agreement vs the Python pipeline
05 · private by design
Your inbox stays yours.
Applied never hands your mail to an LLM. The classifier is a small, purpose-built pipeline that can run entirely in your browser — on that path, your mail never leaves your machine.
no large language model
No LLM reads your mail
Classification is three small layers — regex rules, a compact e5 embedding, and a fine-tuned SetFit head. Nothing in that path calls a third-party model or LLM API, so your email is never handed to one.
It can run in your browser
The trained model compiles to a compact int8-ONNX file that Transformers.js runs on your own CPU (allowRemoteModels = false). Paste text into the in-browser Space and it is classified on-device, never leaving the tab.
least privilege
Read-only, by construction
Connecting Gmail requests exactly one Google scope — gmail.readonly. It can read messages to classify them and cannot send, delete, or modify anything; you authorize on Google's own consent screen.
06 · try it
Run a real email through all three layers.
The pipeline board and decision trace, on fixture data. One click, no sign-in.
Eleven job emails, the real classifier's verdicts, gate, and trace. No inbox read.
Drop a Google Takeout .mbox; it's parsed and classified in your browser. No upload, no sign-in.
Paste your own text; it is classified client-side, on your CPU — nothing uploaded.
The full printed walkthrough: the why, the cascade, and the receipts.
three layers · one gate · zero servers
Signed in, Applied connects your Gmail read-only to classify real mail — tokens encrypted, revocable anytime, nothing sent or deleted.