How the ATS Checking Feature Works: PDF Text Extraction and Validation
The ATS checking feature extracts the text layer from compiled PDF résumés using pdftotext and validates mechanical readability, contact detail accessibility, and keyword coverage against job postings before final submission.
The ATS (Applicant Tracking System) checking feature is a critical validation step in the MadsLorentzen/ai-job-search repository's /apply workflow. It ensures that every generated résumé is parseable by automated hiring systems while maintaining the visual design integrity of the LaTeX template. This process runs automatically after PDF compilation and before the final draft is presented to the user.
What Triggers the ATS Check?
The ATS checking feature activates automatically when the /apply command finishes generating cv/main_<company>.pdf. According to the source code in tools/security_guards.py (line 40), the system first detects the optional pdftotext binary from the Poppler suite and whitelists it for safe execution.
If pdftotext is present on the system, the workflow invokes it immediately; otherwise, it prints a warning to the Step 6 report and falls back to a purely visual keyword review. This behavior is documented in SETUP.md under the section "Optional: pdftotext (for the ATS check)" and in .claude/commands/apply.md under the "Availability check" section.
Extracting the PDF Text Layer
When the binary is available, the ATS checking feature executes the following command to extract the résumé's text layer:
cd cv && pdftotext -layout main_<company>.pdf main_<company>.txt
This command is documented in .claude/skills/job-application-assistant/05-cv-templates.md (lines 182-185). The -layout flag preserves column positions, ensuring that the extracted text mirrors the visual ordering as closely as possible. This preservation is crucial for validating that multi-column layouts or sidebars do not scramble the reading order that ATS parsers expect.
Mechanical Validation Checks
The parser inspects the resulting .txt file for three core ATS-relevant properties, as defined in CLAUDE.md (lines 130-135) and .claude/commands/apply.md (lines 225-230):
Literal Contact Details
Email and phone numbers must appear as plain text (e.g., john.doe@example.com). The validation fails if contact information is embedded only as icons without text labels (such as MOBILE-ALT symbols), since ATS parsers ignore graphical elements.
Glyph Integrity
The extracted text must be Unicode-clean without garbled glyphs. The system checks for (cid:...) placeholders or `` symbols, which indicate fonts lacking proper Unicode mapping. These garbage strings render the résumé unreadable to ATS software.
Reading Order
Single-column layouts must yield a top-to-bottom ordering that matches the visual résumé. Multi-column designs or sidebar-heavy templates can cause extraction order to mismatch visual order, confusing parser algorithms.
If any mechanical check fails, the system flags the template and suggests concrete LaTeX fixes, such as adding \needspace{5\baselineskip} before a \cventry command or moving contact data out of pure icon fonts.
Keyword Coverage Assessment
After mechanical validation passes, the ATS checking feature calculates keyword coverage through a three-step process:
- Harvesting: The job posting's required and preferred terms are extracted by the
/applyworkflow (see the "Job-fit evaluation" section inREADME.md, lines 197-215). - Matching: The extracted text is scanned for exact matches (case-insensitive). Synonyms are not counted unless they appear verbatim in the posting, as many ATS parsers perform literal matching rather than semantic analysis.
- Reporting: A coverage score is generated, and missing required keywords are listed as gaps. The "Honesty rule" enforced by the system prevents the assistant from fabricating keywords that do not appear in the candidate's actual experience.
Integration in the /apply Workflow
The ATS check is the 5d sub-step of the overall workflow, as documented in README.md (lines 215-218) under "How /apply works". It runs after PDF compilation and before the final draft is saved, ensuring that every submitted résumé is ATS-friendly by default.
If pdftotext is missing, the workflow adds a one-line warning to the report and performs the keyword review manually against the visual PDF. When text extraction succeeds, the report contains:
- Pass/fail status for each mechanical check
- A table of matched versus missing keywords
- Suggested LaTeX edits to fix any detected ATS problems
Running the ATS Check Manually
You can replicate the ATS checking feature locally using the following commands:
# 1. Compile the LaTeX CV (already done by /apply)
lualatex -output-directory=cv cv/main_awesomecompany.tex
# 2. Run the ATS text extraction (optional, only if pdftotext is installed)
if command -v pdftotext >/dev/null; then
cd cv
pdftotext -layout main_awesomecompany.pdf main_awesomecompany.txt
# 3. Simple sanity checks (illustrative)
grep -iE 'john\.doe@|john\.doe' main_awesomecompany.txt && echo "Contact OK"
grep -i 'cid:' main_awesomecompany.txt && echo "Garbage glyphs found!"
# 4. Keyword check (example with a posting keyword list)
keywords=("Python" "Machine Learning" "Docker")
for kw in "${keywords[@]}"; do
if grep -iq "$kw" main_awesomecompany.txt; then
echo "✔ $kw"
else
echo "✘ $kw (missing)"
fi
done
else
echo "pdftotext not installed – ATS check skipped (visual review only)."
fi
The real /apply implementation runs these same commands internally and formats the results into a structured report for the user.
Summary
- The ATS checking feature validates the text layer of PDF résumés using the
pdftotextbinary, which is whitelisted intools/security_guards.py. - It performs three mechanical validations: literal contact details, Unicode glyph integrity, and correct reading order.
- Keyword coverage is calculated using exact case-insensitive matches against harvested job posting terms.
- The check runs as step 5d in the
/applyworkflow, with graceful degradation to visual review ifpdftotextis unavailable.
Frequently Asked Questions
What happens if pdftotext is not installed on my system?
If the pdftotext binary is missing, the ATS checking feature prints a warning to the Step 6 report and falls back to a manual visual keyword review against the PDF. The workflow continues but cannot validate mechanical ATS compatibility such as glyph integrity or text extraction order.
Why does the ATS check require exact keyword matches instead of synonyms?
The system requires exact matches because most ATS parsers perform literal string matching rather than semantic analysis. According to the implementation in CLAUDE.md, synonyms are only counted if they appear verbatim in the job posting, ensuring that the validation mirrors real-world ATS behavior.
How does the -layout flag in pdftotext improve ATS compatibility?
The -layout flag preserves the spatial positioning of text columns during extraction, as documented in 05-cv-templates.md. This ensures that the extracted .txt file maintains the same reading order as the visual résumé, preventing multi-column layouts from scrambling into incoherent text sequences that confuse ATS parsers.
Where can I find the whitelisting configuration for the pdftotext binary?
The pdftotext binary is whitelisted in tools/security_guards.py at line 40. This security guard allows the /apply workflow to safely execute the binary during the ATS validation step without triggering execution restrictions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →