verify-ats.mjs under-scores every non-English CV: HTML entities undecoded and English-only heading patterns
Summary
verify-ats.mjs under-scores every non-English CV, by enough to fail the gate. The same CV content scored 99/100 (A) in English and 82/100 (B) in French. The CV was not the problem.
Two independent causes.
1. HTML entities are never decoded
stripInline() strips tags but leaves entities:
function stripInline(fragment) {
return collapse(fragment.replace(/<[^>]+>/g, ' '));
}
A generated CV contains Expérience professionnelle. That reaches the matcher as exp eacute rience professionnelle and matches nothing.
asciiFold('Expérience professionnelle') -> "experience professionnelle" // matches
asciiFold('Expérience professionnelle') -> "exp eacute rience professionnelle" // does not
2. Section-heading patterns are English-only, and lib/ascii-fold.mjs is not used
{ name: 'Experience', re: /experience|work history|employment/ },
{ name: 'Education', re: /education|academic/ },
{ name: 'Skills', re: /skills|competenc|proficienc/ },
Formation never matches education. Compétences would match competenc once folded, but nothing folds it.
This is the third instance of the bug lib/ascii-fold.mjs already documents in its own docstring, which names verify-portals.mjs and providers/_trust-validator.mjs. verify-ats.mjs is a third.
Impact
sections carries 20 of the 100 points. A French, German, Arabic, Japanese or Turkish CV loses 15 of them plus most of the bonus, so a structurally perfect CV scores in the low 80s. Since career-ops ships modes for fr, de, ar, ja and tr, every user of those modes is affected. The failure is silent: it reads as a defect in the user's CV, and the obvious "fix" is to anglicise the headings of a French CV, which is the wrong outcome.
Reproduce
node build-cv-onepage.mjs # or any pdf-mode build
node verify-ats.mjs output/<cv>-en.html # 99/100
node build-cv-onepage.mjs fr
node verify-ats.mjs output/<cv>-fr.html # 82/100, critical: missing Experience, Education, Skills
Fix (tested locally, both languages now 99/100, English unchanged)
- Decode entities inside
stripInline()before collapsing (numeric, hex, and the Latin-1 named set). asciiFold()each heading inextractHeadings()before matching, since the patterns are ASCII by construction.- Extend the required and bonus patterns to the languages the repo ships modes for:
formation,diplome,kenntnisse,beceri,職歴,المهارات, and so on.
node test-all.mjs shows no regressions from this change (same pre-existing failures before and after).
Happy to open a PR if you want it in this shape.
Source: santifer/career-ops