Skip to content
Meritline

methodology

How Meritline ranks

This page describes what the shipped code does, including the parts that are not finished. If something here is not verifiable in the product, it is a bug in this page — tell us.

1 · The pipeline

Everything below happens in your browser. No part of it runs on a server.

  1. Extract. Text is pulled out of each PDF, DOCX or TXT file locally (pdf.js and mammoth).
  2. Gate. A fixed keyword-and-structure heuristic checks whether the file looks like a CV. It does not remove anyone: a file it doubts is still ranked, and its card says “Set aside for review” with the reason. Only a file with no readable text is left out, and it is listed with what to do about it.
  3. Embed. The job description and each CV are turned into 384-dimension vectors by all-MiniLM-L6-v2, an INT8-quantised ONNX model that runs on WASM in a Web Worker. The first run downloads the model and one runtime file — about 33 MB in total, of which the model is 23 MB — from this site, self-hosted and SHA-256-verified before use, and caches them in your browser.
  4. Score. Cosine similarity between the job-description vector and each CV vector. That single number is the match index.
  5. Rank. Sorted by similarity at full precision, ties broken by filename using a fixed byte-order comparison.

Alongside the score, a static dictionary of skill terms is matched against the job description and each CV to show which required skills appear. That matching is a display aid — it does not change the score.

2 · What is and isn't deterministic

There is no language-model call, no sampling, no temperature, and no randomness anywhere in the score path. Given the same job description, the same CV text and the same model, the pipeline produces the same number.

The honest limits of that claim:

  • The model file is served from this site and its SHA-256 is checked before it is used — see verify. A different file would stop the run rather than change the score.
  • Until 10 September 2026 this was only true for the whole upload: the model processed every CV in one batch, and its INT8 arithmetic made each CV's score depend slightly on the other CVs beside it (up to 0.0125 in cosine on our test set). Each CV is now scored in its own call, so its score depends on that CV and the job description alone. Receipts made before the change carry the rubric cosine-v1; later ones carry cosine-v2, then cosine-v3 once identity was taken out of the scored text, and cosine-v4 since where a candidate worked left it too (§4).
  • Text extraction depends on the PDF library version. A different version can extract slightly different text from the same PDF, which would change the score.
  • INT8 inference on WASM is deterministic on a given build, but we have not yet published a cross-browser reproduction test. Until we have, treat "identical on any machine" as untested rather than proven.

The score_id is sha256(sha256(jd) + sha256(cv) + model id + rubric), where cv is the text the model scored — identity removed (§4). Two runs over the same inputs produce the same id, so you can tell whether a number came from the inputs you think it did. Two CVs that differ only in name or contact details get the same id, because they get the same score.

score_id example6b6ddb79d123…43f81b

3 · A match index, not a probability

The match index orders candidates against this job description. It is not a percentage, a probability, or a prediction that someone will be hired.

Cosine similarity is one holistic number. It is not broken into sub-scores because there are no sub-scores to show — anything presented as a component breakdown of this number would be manufactured. The matched and missing skill lists are separate, honestly-derived information.

4 · What the scorer sees

Read this if you are assessing bias exposure. Before a CV is scored, the candidate's name, contact details, home city and personal details (date of birth, nationality, marital status, religion, caste category, gender, parents' names, passport place of issue, blood group, and height and weight unless your job description sets them) are taken out of the text the model reads, along with photo lines, faith affiliations, gendered pronouns, career-break entries, the names of schools and universities (the degree and subject stay), and any language your job description does not ask for. This happens on every run, whatever you choose to see on screen, and the CV you read is unchanged. It is best-effort removal of what is written in recognisable places — see the known gaps below.

Until 10 September 2026 this was not so: the model read the CV in full unless blind screening was switched on, and blind screening removed only names and contact details. Blind screening — now on by default — only decides what you see. Because signals remain, the adverse-impact monitor still measures outcomes after the fact.

We test this on invented CVs by changing one thing at a time. Before identity was taken out of the scored text, changing only the name moved a candidate down in 157 of 420 tests, by up to 25 places, and changing only the home city moved one down in 131 of 273, by up to 42. Now, changing the name, home city, a career break, nationality, age, marital status, religious affiliation, caste category, parents' names, passport place of issue, height, weight, blood group, a photo line, gendered wording, the university, a language your job description does not ask for, where a candidate worked, or the country of a past employer moves nobody, in plain-text and PDF-shaped CVs alike. Where a candidate worked was the strongest of these before it was removed: it moved the CV down in 267 of 360 tests, worst for Kochi, Kathmandu, Dhaka and Manila. That is a statement about those changes, on those CVs, in the layouts we used — not about every CV.

We also test names and layouts the rules were not written around: initials, single names, Bahadur and Kumar middle names, bin and Al- names, names in capitals, names that are also ordinary words, and names that appear only in an email address. On the most recent of these sets — new names, new cities and new layouts, built after the rules were final, checked against the detector's own source, and measured once — the name and the home city were both removed in 100% of 4,788 cases, including where the city sits in a trailing personal-details block or in an opening sentence rather than the header. Changing who the candidate was still changed the score in 6 of 1764 tests, moving one candidate down by two places. Two other layout sets, measured in the same run, still leak a home city — see the known gaps.

Known gaps

  • A home city written outside the header — a contact block at the foot of the CV, or an "about me" paragraph — can still reach the model. On two other held-out layout sets, measured at the same time, it was removed in 89% of cases, and in 77% when the CV arrives as one line of flattened PDF text. The set built around a trailing details block and an opening "residing in" sentence was clean; these two layouts are not.
  • Employers are kept, so a well-known regional employer can still hint at where someone has worked, even though the location itself is removed. Taking employers out would remove real evidence of experience.
  • A name with no email address or filename to confirm it can be missed when it is a single word or is also a job title, such as Mason.
  • Everything here was tested on invented CVs in English. Real CVs were not used, and scanned CVs were not tested at all — a scan cannot be read today, so there is nothing to test.
  • Signals that are not removed: how a CV is written, and which skills a person lists.

English CVs only, for now. The model was trained on English text, so a CV in Arabic, Hindi or another language is not read meaningfully and will rank low.

The full tables behind these figures — every swap family and every held-out slice — are on the identity tests page.

5 · What leaves your device

CV contents are scored on your device and are never uploaded.

Signing in sends your Google identity. Recording a decision sends a score id and the action — never CV text, names, or filenames.

In full, the network requests a ranking run makes are: the one-time model and runtime download from this site (self-hosted and hash-verified — see verify), Google sign-in, a session check, and — when you shortlist someone or mark them not a fit — a small message containing a score id and the action name. CV text, candidate names and filenames are not in any of them.

6 · The state of the audit record

Scores and decisions are recorded in this browser only. They are reproducible from the same inputs, but they are not yet signed by a server, so they cannot prove to a third party when they were made.

Concretely: a receipt today proves reproducibility (re-run the inputs, get the same id) but not custody (nothing independent attests to when the run happened or that the result was not edited afterwards). Server-signed receipts are the next piece of work.

7 · Human-in-the-loop

Meritline recommends — you decide. Rankings are a starting point — every action stays with the recruiter. Nothing here rejects a candidate on its own.

Meritline records the actions you take — open · shortlist · reject (the button says “Not a fit”) — so the run has a decision history. They are never fed back into the ranking. There is no auto-reject and no path where the software decides.

There is deliberately no "advance" action. Moving a candidate through stages is what an applicant tracking system does, and Meritline hands off to yours rather than competing with it. Runs recorded before this was removed may still show an advance in their history.

8 · What has actually been measured

On a 252-CV benchmark across 7 job descriptions, Recall@20 is 0.857 (95% CI 0.757–0.900), Spearman rho is 0.740 (0.684–0.777) and Precision@5 is 0.286 (0.143–0.457). That rho fails the 0.900 floor we set ourselves, and we have not moved the floor. The reference ranking is constructed by the corpus generator rather than written by people, so it measures agreement with a specification, not with hiring judgement. That is why results are grouped into bands rather than presented as an exact order.

What that benchmark cannot tell you:

  • The reference ranking is constructed, not human. Every CV is generated from stated attributes, and the “right” order is those attributes scored by the generator’s own weights. So the number measures agreement with a specification, and it is circular in a way human annotation would break. Kendall’s W — the statistic that would tell you whether people even agree with each other about this — is pending human annotation, and we report it as pending rather than inventing one.
  • It scores text that is already extracted, so it measures the ranker, not the PDF and DOCX extraction in front of it.
  • It runs on onnxruntime-node, not the WASM runtime your browser uses, and the CVs are synthetic prose rather than real writing.

The other figure quoted about Meritline is a Spearman rank correlation of 0.9286 against a hand-written ranking of 21 CVs. That is a determinism fixture, not an accuracy result: it is what we re-run to prove that blocking every external origin changes nothing about the output. Read as accuracy it flatters us by a wide margin, because the CVs are synthetic, the hand ranking was written by the same person who designed them, and 21 candidates on one role is a sample small enough that a single position moves the score. The 252-CV corpus above is harder, larger and more representative, which is why it — and not 0.9286 — is the number we stand behind. 0.9286 was measured in the browser before identity left the scored text (§4). Scored headlessly on onnxruntime-node, the same 21 files gave 0.9221 then and 0.874 now.

These figures come from the committed baseline, which was measured before identity, institutions and where a candidate worked left the scored text. They therefore describe an earlier configuration than the code you are running. The baseline is re-run once after a series of changes rather than after each one, so the published figures stay comparable; against these published figures the current code scores higher on all three, and the exact figures are recorded with the change that caused them. The 0.90 floor fails either way, and we have not moved it.

The model these numbers describe is all-MiniLM-L6-v2, pinned and hash-verified — see verify for the hashes and the reproduction evidence.