Reading résumés
Extract the text, refuse to score what cannot be read, redact identifying details, then score against the rubric with a verbatim quote behind every criterion and a gap list the screen goes after.
The problem it solves
Résumé screening is where most of the volume and most of the unexamined judgement sits. A keyword filter rejects the person who wrote "Apache Kafka" for a requirement labelled "Kafka pipelines", and passes the person who wrote "no Kafka experience". A model reading the document does better — but a model reading a document also produces confident prose about a person, and if you cannot check what it read, you cannot check the score.
So the pipeline is built around checkability. Every criterion cites the sentence it came from, the model is not shown the attributes it must not score on, and the document is refused outright if the text is too poor to score against.
How it works
1. Extract, and judge the extraction
The file is turned into text, and the text is judged before anything scores against it.
A scanned page that OCRs into forty characters of noise is a successful parse holding text no scorer can use. Screening someone against it would be exactly the failure that is invisible from the outside. So the gate measures two things: how many alphabetic characters came out, and what share of the non-whitespace characters those are. Length alone passes a page of OCR garbage; ratio alone passes a clean forty-character header from a PDF whose body never came out.
If the text fails the gate, the file is parsed once more at a higher-quality tier, and judged again. Once, not repeatedly: a second failure at the better tier is evidence about the document, not about the parser.
Password-protected files are detected before parsing rather than after, because a parser will happily bill for an encrypted PDF and hand back its empty readable layer. Plain text and markdown files skip the gate entirely — they were decoded, not parsed, so there is no extraction failure to catch and no better tier to retry at.
A document that still cannot be read produces a reason, and the reason is what the candidate's email is written from. "We could not read your résumé" is useless advice when the fix is "send it without the password".
How the file was read (the quality tier, whether it was retried, and the quality numbers) is stored with the candidate, so a decision made from a marginal read can be explained months later.
2. Redact
Before the model sees the document, the following are removed and replaced with typed placeholders: the candidate's name, email addresses, phone numbers, URLs (a personal site or a profile slug carries a name and often a photo), lines that look like a street address, dates of birth, and graduation years.
Employment dates stay. "Staff Engineer, 2019–2024" is how long someone did the job, which is exactly what a rubric asks about. Years are stripped only from lines that also name a degree — the ones that date a person rather than their work.
Every rule over-removes rather than under-removes. A candidate called Grace loses the word "grace" from her prose, which costs a shade of readability; leaving one email address in costs the control.
Spans become placeholders rather than vanishing, so the model reads a document with a hole in it rather than a sentence that has quietly changed meaning — and so a quote it copies back verbatim can still be matched against the text.
The prompt separately forbids inferring protected characteristics or their proxies. Both are kept, because they fail differently: redaction cannot remove an inference, and a prompt rule cannot un-see an address. The redaction version is stored on the assessment, because a score is only reproducible if you know what the scorer was allowed to see.
3. Score
The model scores the redacted document against the job's rubric and description. For each criterion it returns a status, a score, a rationale and verbatim quotes from the résumé, all of them kept. It also produces the gaps — what the document failed to establish — which is what the AI voice screen is later pointed at.
Five rules shape the number:
- A criterion the résumé never mentions scores zero and is kept. This is the deliberate inversion of the interview rule, and it is the most consequential decision in résumé scoring. A transcript that never asked about mentoring is silent; a résumé is the candidate's own account of themselves, written for this kind of role, so its silence is weak evidence. Because of this inversion, a résumé score and an interview score are not comparable and are never averaged together, though the job's same two numbers are read against each.
- Except a criterion no résumé could speak to, such as whether someone can work from a particular office. It is left out of the score and out of its denominator, writes no score row, and goes on the gap list for the screen. Scoring it zero would mark down every résumé ever written.
- A contradicted deal-breaker caps the overall score at 39. The résumé actively says otherwise, with a quote. Nothing else in the document buys that back.
- A missing deal-breaker does not cap. The résumé is silent, not damning — it might simply be badly written. It costs its full weight and becomes the first thing the voice screen asks about. Capping on silence would reject people for how they write about themselves.
- Coverage is the share of rubric weight the résumé addressed — evidenced, partial or contradicted. Only "missing" is uncovered. A criterion the document half-answered or argued badly was still spoken to.
Coverage is computed, stored and shown at this stage, but the résumé decision gate does not read it. That is deliberate: a résumé is a complete document, so low coverage is a finding about the candidate rather than about how much of them anyone got round to measuring. No stage gates on coverage. See Deciding.
4. The fallback
If the model produces nothing usable (no rubric, no résumé text, or an outage), a simple keyword scorer runs instead and its result is final.
That scorer does not read the résumé; it looks for the words. It has no evidence, no coverage and nothing to audit but the labels that happened to appear.
Falling back is still the safer behaviour. An application with no final score is one that no automation can decide in either direction, so a model outage that "safely" did nothing would strand real candidates indefinitely. A fallback score is marked as one everywhere it appears, because anyone reading it is entitled to know it was not the model.
What the user sees and does
- A candidate on the board shows Scoring… until the score lands.
- The score opens into a per-criterion breakdown: the status, the weight, the rationale and the quotes it came from.
- The gap list is visible and is what the AI screen will probe.
- Scoring state is shown as final or fallback, and a held candidacy shows why it is held.
- A recruiter can re-upload a corrected résumé, which reopens the score.
Limits and edge cases
- A résumé score is not an interview score. They are produced under opposite rules about silence, even though the same two numbers are read against each. Do not compare them or average them.
- Redaction is pattern-based and imperfect. It errs toward removing, which costs readability and can remove ordinary words that collide with a name.
- The model can still infer. Redaction removes attributes; it cannot remove inference from employer names, school names left in prose, or writing style. The prompt rule is the second control, and neither is a guarantee.
- A fallback score decides a candidacy exactly as a real one does. It is compared against the same thresholds. The mitigation is that it is visibly marked, not that it is treated differently.
- There is no coverage floor at the résumé stage. A barely-readable résumé that clears the readability gate is scored and can be rejected on that score. A rejected candidate can ask for a human review.
- An unreadable résumé cannot be auto-rejected. It is never scored, so nothing automatic can end that candidacy.
- Quotes must be verbatim. A criterion the model cannot quote for is dropped rather than kept as an unevidenced assertion.