mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 00:26:26 +00:00
feat: add ATS & keyword verification pass to /apply's compile-and-inspect loop (#40)
An ATS reads the compiled PDF's embedded text layer, not the rendered page, and LaTeX can silently produce PDFs whose text extracts as garbage: icon glyphs where contact details should be, (cid:*) markers from fonts without Unicode mappings, interleaved lines from multi-column layouts. This matters more now that /add-template lets users bring arbitrary templates. The existing Step 5 loop verifies what a human sees; this adds verification of what a parser sees. New Step 5d in /apply (CV only - cover letters rarely go through keyword screening; cleanup renumbered to 5e): - Extract the CV PDF's text layer with pdftotext -layout. pdftotext (poppler) is an optional dependency: if missing, the mechanical check is skipped with a warning and keyword coverage falls back to the visual PDF read - the same graceful-skip pattern as salary_lookup.py - Parseability checks verified against a real extraction of the stock template: email/phone must survive as literal text (fontawesome icons extract as harmless glyph-name noise like MOBILE-ALT/Envelope, but a contact detail carried only by an icon or hyperlink is invisible to ATS), no (cid:*) or replacement-character garbage, reading order matching visual order, dates present - Keyword coverage reuses the required/preferred list from Step 1, matched in the posting's language, reported as covered / synonym-only / missing-have-it / missing-gap. Honesty rule enforced: keywords the profile genuinely supports get added to experience bullets; genuine gaps stay visible, never stuffed Integration: CLAUDE.md verification checklist section, ATS Parseability guidance in 05-cv-templates.md, narrow Bash(pdftotext:*) entry in the pre-approved permissions (keeping with the tightened scope from #27), cv/*.txt gitignored (extraction is personal data; also deleted by the step itself), and optional-dependency docs in README and SETUP.
This commit is contained in:
@@ -40,6 +40,7 @@ The framework encodes career guidance best practices, including structured evalu
|
||||
- Python 3.10+
|
||||
- [Bun](https://bun.sh) (for Danish job search CLI tools)
|
||||
- LaTeX distribution with `lualatex` and `xelatex`: [TeX Live](https://tug.org/texlive/) or [MiKTeX](https://miktex.org/). The CV compiles with `lualatex` (pdflatex often fails on modern MiKTeX installs with `fontawesome5` font-expansion errors); the cover letter compiles with `xelatex` because `cover.cls` requires `fontspec`.
|
||||
- Optional: `pdftotext` from [poppler](https://poppler.freedesktop.org/) (macOS: `brew install poppler`, Debian/Ubuntu: `apt install poppler-utils`, Windows: `choco install poppler`) — used by `/apply`'s ATS parseability check on the compiled CV. If missing, the check degrades gracefully to a visual keyword review.
|
||||
|
||||
## Quick start
|
||||
|
||||
@@ -171,13 +172,15 @@ The `/apply` command runs a **drafter-reviewer workflow** with mandatory PDF com
|
||||
4. **Spawn a reviewer agent** that researches the company and critiques the drafts
|
||||
5. **Revise** based on the reviewer's feedback
|
||||
6. **Compile and inspect** both PDFs: lualatex for the CV, xelatex for the cover letter. Claude reads the rendered pages and iterates on the LaTeX until the CV is exactly 2 pages with no orphaned entry titles, and the cover letter is exactly 1 page with the signature visible and fonts consistent.
|
||||
7. **Present** the final output with a verification checklist
|
||||
7. **ATS-check the CV**: extract the PDF's text layer (`pdftotext`, optional dependency) and verify it the way an ATS parser sees it — contact details present as literal text, no garbled glyphs, sane reading order — then score the posting's keyword coverage against the extraction. Keywords the profile genuinely supports get added; genuine gaps stay visible, never stuffed.
|
||||
8. **Present** the final output with a verification checklist
|
||||
|
||||
All claims in the CV and cover letter are verified against your actual profile. The system never fabricates skills or experience.
|
||||
|
||||
### What makes this workflow different
|
||||
|
||||
- **PDF verification loop.** Most LaTeX-resume templates produce "looks fine in the .tex" output that breaks in the PDF: job titles orphan to the next page, cover letters spill onto page 2, bullet fonts silently fall back to the body font. The `/apply` command compiles and visually inspects every PDF and applies targeted fixes (`\needspace`, `\enlargethispage`, font-matching wrappers for list items) until the layout is clean. This runs automatically on every application.
|
||||
- **ATS verification on the PDF text layer.** An ATS reads the PDF's embedded text, not the rendered page — and LaTeX can silently produce PDFs whose text extracts as garbage (icon glyphs where the email should be, interleaved lines from multi-column layouts). `/apply` extracts the compiled CV's text layer with `pdftotext` and verifies contact details, reading order, and the posting's keyword coverage against what a parser actually sees. Honesty rule enforced: a keyword the profile doesn't support is acknowledged as a gap, never stuffed in.
|
||||
- **Relevance-weighted CV cutting.** When a CV overflows 2 pages, the workflow does not cut mechanically from the "oldest" section. It scores each candidate line by (a) relevance to the target posting, (b) uniqueness in the document, and (c) whether the cover letter depends on it, and cuts the lowest-total-score line first. An older-role bullet that hits posting keywords survives ahead of a recent-role bullet that does not.
|
||||
- **Drafter-reviewer separation.** The drafter writes; a second Claude agent, spawned with a fresh context, researches the company and critiques the drafts. The drafter then revises. This catches missed keywords, weak framing, and generic language that a single pass often leaves in.
|
||||
- **Token-efficient reviewer dispatch.** The reviewer agent receives drafts inline rather than re-reading them, and the verification checklist runs once at the end of the workflow rather than being duplicated by both agents. Note: the new compile-and-inspect step in Step 5 spends some of those savings on PDF rendering and layout iteration — the workflow trades some end-to-end token cost for a real reduction in broken PDFs reaching the user.
|
||||
|
||||
Reference in New Issue
Block a user