How the parse score is calculated
The parse score measures one thing: how well your CV survives being read by an applicant tracking system. It is not a judgment of your experience, your writing, or your fit for any role. A brilliant CV in a layout that parsers mangle scores low, and that's the point.
method version: parse-score/v2
The mechanics
We run your CV through the same commercial parsing engine that powers real ATSs, then inspect the result for likely mis-parses. Each finding becomes a visible signal in the preview, and each signal does a measured amount of damage to the section it belongs to. The score is nothing but a roll-up of the signals you can already see; there are no hidden factors. Three rules determine how much damage a signal does.
1 · Base damage by severity
| severity | base damage | meaning |
|---|---|---|
| error | −55% | the parser got this factually wrong (a misread skill, a swapped field) |
| warning | −22% | worth double-checking (missing dates, a low-confidence read) |
| info | −5% | a quiet note (minor normalization oddities) |
Base damage is the share of a section's score a signal removes if it affects the whole section. These values are chosen judgment calls, not yet derived from data. We say so here, and we're calibrating them against a corpus of real engineering CVs.
2 · Coverage scaling
A problem on one item out of many hurts less than a problem across the whole section: one undated role out of five does one-fifth of the base damage, so fixing four of five undated roles visibly moves your score. The divisor is capped at 10 because keyword-search damage is absolute (a recruiter searching the one skill that was misread gets zero hits), so a long skills list can't dilute a real misread to nothing.
Signals that carry the parser's own confidence scale with it: a read the parser was only 40% sure of does about 1.35× the damage of one just under the 75% flag threshold.
3 · Diminishing returns
Damage compounds multiplicatively: each signal removes its share of what remains, so a section's score is 100 × ∏(1 − damage). Two whole-section errors leave ~20, three leave ~9. A section degrades toward a floor instead of slamming into zero, so the score keeps discriminating between bad and worse, and improvements always register.
Empty sections
If the parser extracts nothing for a core section, we check the raw extracted text for that section's heading. If the heading is there but the content didn't parse, that's the most serious failure we can detect and the section loses 95% of its score. If the CV genuinely doesn't have the section, like a new grad with no work history, it's excluded from the roll-up entirely and noted as an advisory instead. Projects are always optional.
Section weights
| experience | 40 |
| skills | 25 |
| contact | 20 |
| education | 15 |
| projects | 10 |
The overall score is the weighted average of section scores, with weights rescaled over the sections your CV actually has. Experience weighs most because it's what recruiters search; contact matters because a mis-parsed email means no reply at all.
Grades (provisional)
| A | 90–100 |
| B | 75–89 |
| C | 60–74 |
| D | 40–59 |
| F | 0–39 |
These bands are provisional round numbers. Once enough real CVs have been scored, we'll set them where they mean something ("B = parses cleanly enough that a recruiter sees the real CV") and bump the method version. We'd rather admit that than present arbitrary thresholds as science.
What the signals detect
- missing essentials: no name or email (error), no phone (warning), no location (note).
- field swaps: an employer that reads like a job title, or a title that reads like a company name.
- missing dates & empty entries : undated roles or education, roles with no bullet points.
- duplicated entries: the same role or education entry appearing twice, whether your document lists it twice or the parser split one entry in two. A BS and MS from the same school, or a return stint at a company, are not flagged.
- low parser confidence: the engine itself reports how sure it is per field; anything under 75% is flagged with its confidence shown, and lower confidence does proportionally more damage.
- misread skills: the parser normalizes skills against a taxonomy, and sometimes replaces what you wrote with something else entirely (we've seen Supabase become "Subbase" and GCP become "Good Clinical Practices"). Correct normalizations like acronym expansions, vendor prefixes, and known synonyms are not flagged.
Content lint: findings, never a grade
Alongside the parse signals, Cvlinter lints the CV's substance, and deliberately does not score it. Parseability is measurable, so it gets a number; writing quality is a judgment, and a standalone "content: 62/100" is exactly the fictional ATS number this tool exists to counter. Content lint therefore aggregates the way a code linter does: a tally of specific, itemized findings ("3 things to fix, 5 to consider"), each traceable to a specific bullet. to fix means a recruiter-visible weakness; to consider is a convention worth knowing about.
- weak openers: bullets starting with "Responsible for", "Worked on", "Helped". Responsibility phrasing, not impact.
- no measurable outcome: a role where no bullet contains a single number, percentage, or amount.
- bullet length: over 45 words (stops being skimmable) or fragment-short.
- first person: "I"/"my"; CV convention is implied first person.
- skills coherence: both directions: skills listed but demonstrated in no bullet, and software evidenced in bullets but missing from the skills section.
- summary: buzzword self-description ("passionate", "results-driven"…), over-long summaries, first person. We lint the summary you wrote; we never generate one for you.
- format: more than 2 pages; an apparent two-column layout (parses fine in some ATSs, badly in others). A scanned CV is flagged in the parse lane instead: OCR genuinely degrades extraction.
Three further rules are judgment calls no regex can make, so when enabled a small language model classifies each bullet against them: responsibility-phrasing (describes a duty, not an outcome, beyond just a weak opener), vague-claim (an achievement asserted with no number, scale, or specifics), and tense-inconsistent. The model classifies only: its output schema has no field for suggested text, so it structurally cannot rewrite anything you wrote. One batched call per CV, cached, and if it fails you simply get the deterministic findings alone.
Content findings never affect the parse score. That's enforced in code, not by policy. The wordlists behind these rules are early and under review by a working tech recruiter. The one place a content-adjacent number is honest is relative to a specific job description. That's the JD match, below.
JD match: the one honest number
Paste a job posting and we extract its concrete requirements (at most 12: technologies, experience, responsibilities; boilerplate ignored) and classify each one against your CV:
- evidenced: a bullet shows you actually doing it. The bullet is quoted as the receipt. Semantic matches count; stretch matches are instructed away and a hard rule backs that up: a requirement can only be "evidenced" by a work or project bullet.
- mentioned: it appears only in your skills list or summary. Claimed, never shown; a skills-list entry can never count as evidence. That rule is enforced in code after the model answers, not left to the model.
- missing: nothing in the CV supports it. We say so plainly.
The headline, "you evidence 6 of 10 requirements", counts only evidenced. It's defensible precisely because it's anchored to one concrete posting; it is not a general quality score, and the same CV will legitimately score differently against different roles. A missing requirement is not an instruction to add keywords: the classifier's output schema has no field for suggested wording, so keyword advice structurally cannot come out of it. (Method version: jd-match/v2, same small language model as the lint rules, deterministic settings, cached.)
The fix flow: your words, tightened
Findings can be fixed without us writing your CV for you. For a flagged bullet we ask what you actually did; a language model then tightens your bullet using only the material in it and your answers. The guardrails are structural: the output schema is a single revised sentence, and a code-side fact guard rejects any suggestion containing a number that doesn't appear in your own words, before you ever see it. Nothing is applied without your explicit approval, and nothing you type is stored. (Method version: fix-flow/v2.)
The ATS-safe export: one template, forever
The export renders your parsed content, plus the fixes you approved and nothing else, into a single deliberately classical layout: one column, embedded real text, plain uppercase headings. This is not a resume builder: there is exactly one template and no design options, because its only job is to parse cleanly. Your skills section is reproduced from your CV's own text, never from the parser's normalized skill list (which can invent terms you never wrote). The PDF is generated in memory and streamed to you; we keep nothing.
What we deliberately don't do
- No quality grade. Content lint itemizes concrete findings; it never rolls them into a "your CV is X/100" judgment.
- No keyword-stuffing advice.
- No hidden factors. Every signal's damage is itemized in the score card as the percentage of its section it removed.
- No storage: your CV is parsed in memory, discarded, and never used to train anything. To calibrate this method we record which checks fired and what the sections scored. Counts only, never your CV's content.
Versioning
The score you see carries its method version (currently parse-score/v2). When weights or rules change, the version changes and this page is updated in the same release. A score is only meaningful if you can see the method that produced it.