How the /apply Drafter-Reviewer Workflow Works Internally in ai-job-search
TLDR: The /apply command executes a deterministic six-step pipeline where a drafter agent evaluates job fit, generates LaTeX documents, and a separate reviewer agent audits factual accuracy—producing ATS-ready CVs and cover letters through structured feedback loops and mandatory compilation checks.
The drafter-reviewer workflow in the MadsLorentzen/ai-job-search repository automates the creation of tailored job application materials through a rigid orchestration defined in .claude/commands/apply.md. This architecture isolates content generation from critical review, ensuring that every output is factually grounded against the candidate's static profile while maintaining strict safety boundaries against untrusted job posting data.
The Six-Step Drafter-Reviewer Pipeline
The workflow progresses through seven distinct phases (numbered 0–6) that transform a raw job posting URL or text into polished PDFs.
Step 0: Parse Input and Establish Safety Boundaries
In .claude/commands/apply.md (lines 15–22), the pipeline first detects whether the user provided a URL or raw text. If a URL is supplied, the system fetches the posting but treats the content as untrusted data—explicitly refusing to follow any embedded instructions, hidden URLs, or malicious directives. This step extracts essential metadata including company name, role title, department, location, and language, which serve as the "trust anchor" for subsequent stages.
Step 1: Evaluate Fit and Benchmark Salary
The Drafter agent begins by reading the candidate's static profile from .claude/skills/job-application-assistant/01-candidate-profile.md and the evaluation rubric from .claude/skills/job-application-assistant/04-job-evaluation.md (lines 25–48). If available, it executes python salary_lookup.py "<Company>" to enrich the analysis with market compensation data. The drafter produces a structured fit report scoring skills, experience, culture alignment, and salary expectations, then pauses for user confirmation before proceeding.
Step 2: Draft CV and Cover Letter Documents
Leveraging the writing style guide in .claude/skills/job-application-assistant/03-writing-style.md and LaTeX template specifications from .claude/skills/job-application-assistant/05-cv-templates.md and .claude/skills/job-application-assistant/06-cover-letter-templates.md, the drafter creates two new files: cv/main_<company>_<role>.tex and cover_letters/cover_<company>_<role>.tex (lines 54–81). This stage applies "grounding audit" rules ensuring every claimed fact matches the master profile stored in cv/main_example.tex, while respecting page limits and language policies.
Step 3: Research and Content Critique
A fresh Reviewer agent spawns with the drafts embedded inline (no additional file reads). Operating solely from the company name and pre-approved reference files—never the raw posting text—the reviewer conducts independent company research and performs a factual grounding audit against .claude/skills/job-application-assistant/02-behavioral-profile.md. It returns Part A (machine-readable JSON edit objects) and Part B (narrative suggestions) for the drafter to implement (lines 95–73).
Step 4: Apply Structured Feedback
The drafter executes the reviewer's JSON edits via the Edit tool, replacing specific old strings with new content without re-reading the draft files (lines 78–89). It manually addresses Part B suggestions by closing keyword gaps, refining company-specific angles, and adjusting tone according to the writing style guide.
Step 5: Compile PDFs and Verify ATS Compatibility
This mandatory compilation phase uses lualatex for the CV and xelatex for the cover letter (lines 95–73, 95–71). The drafter inspects generated PDFs for layout violations—such as orphaned titles or page-break issues—and iteratively edits the .tex files until the layout checks pass. Finally, it runs an ATS verification pass using pdftotext to extract the text layer and validates keyword coverage against the list built in Step 1.
Step 6: Present Final Output and Next Actions
In the concluding phase (lines 80–99), the system re-runs the global verification checklist defined in CLAUDE.md, lists the generated file paths, and suggests follow-up commands (/outcome or /interview) to continue the job-search lifecycle.
Implementation Logic and File References
The pipeline relies on a deterministic orchestration that external systems could mirror. Below is a pseudo-Python outline showing how these steps translate programmatically:
import subprocess, json, pathlib
from pathlib import Path
def parse_input(arg):
# Step 0 – URL detection with safety boundaries
if arg.startswith("http"):
posting_text = webfetch(arg) # Claude WebFetch tool
else:
posting_text = arg
meta = extract_metadata(posting_text) # company, role, language
return posting_text, meta
def evaluate_fit(meta, posting):
# Step 1 – Drafter reads profile and rubric
profile = read_file(".claude/skills/job-application-assistant/01-candidate-profile.md")
rubric = read_file(".claude/skills/job-application-assistant/04-job-evaluation.md")
salary = None
try:
result = subprocess.check_output([
"python", "salary_lookup.py", meta["company"], "--json"
])
salary = json.loads(result)
except Exception:
pass
fit_report = drafter_evaluate(profile, rubric, posting, salary)
return fit_report
def draft_documents(meta, posting, fit_report):
# Step 2 – Generate LaTeX using templates
style = read_file(".claude/skills/job-application-assistant/03-writing-style.md")
cv_tpl = read_file(".claude/skills/job-application-assistant/05-cv-templates.md")
cl_tpl = read_file(".claude/skills/job-application-assistant/06-cover-letter-templates.md")
cv_tex = drafter_generate_cv(meta, fit_report, cv_tpl, style)
cl_tex = drafter_generate_cover(meta, fit_report, cl_tpl, style)
cv_path = Path(f"cv/main_{meta['company']}_{meta['role']}.tex")
cl_path = Path(f"cover_letters/cover_{meta['company']}_{meta['role']}.tex")
cv_path.write_text(cv_tex)
cl_path.write_text(cl_tex)
return cv_path, cl_path
def reviewer_feedback(cv_path, cl_path, posting, meta):
# Step 3 – Reviewer audits with isolated context
cv_tex = Path(cv_path).read_text()
cl_tex = Path(cl_path).read_text()
feedback = reviewer_agent(
company=meta["company"],
role=meta["role"],
posting=posting,
cv_draft=cv_tex,
cover_draft=cl_tex,
)
return feedback # Part A (JSON) + Part B (narrative)
def apply_feedback(feedback, cv_path, cl_path):
# Step 4 – Execute structured edits
for edit in feedback["part_a"]:
edit_file(edit["file"], edit["old_string"], edit["new_string"])
# Part B applied via manual revision
def compile_and_verify(meta):
# Step 5 – LaTeX compilation and ATS checks
subprocess.run(["lualatex", f"cv/main_{meta['company']}_{meta['role']}.tex"])
subprocess.run(["xelatex", f"cover_letters/cover_{meta['company']}_{meta['role']}.tex"])
ats_check(meta) # Uses pdftotext for text extraction
def final_output(meta):
# Step 6 – Verification and handoff
print(f"✅ CV ready: cv/main_{meta['company']}_{meta['role']}.tex")
print(f"✅ Cover letter ready: cover_letters/cover_{meta['company']}_{meta['role']}.tex")
Critical Configuration Files
The workflow depends on these specific source files:
.claude/commands/apply.md– Master pipeline definition (steps 0–6).claude/skills/job-application-assistant/01-candidate-profile.md– Static candidate facts and employment history.claude/skills/job-application-assistant/04-job-evaluation.md– Rubric for scoring job fit.claude/skills/job-application-assistant/03-writing-style.md– Tone and voice guidelines.claude/skills/job-application-assistant/05-cv-templates.md– LaTeX CV structural specifications.claude/skills/job-application-assistant/06-cover-letter-templates.md– Cover letter layout rules.claude/skills/job-application-assistant/02-behavioral-profile.md– Behavioral reference for reviewer auditssalary_lookup.py– Optional compensation benchmarking toolcv/main_example.tex– Master CV template serving as the grounding baselineCLAUDE.md– Global verification checklist for final validation
Summary
- The drafter-reviewer workflow separates document generation from critical audit, preventing hallucinations by grounding all claims in
.claude/skills/job-application-assistant/01-candidate-profile.md. - Step 0 enforces safety by treating job postings as untrusted data, extracting only metadata before any processing.
- Steps 1–2 use specialized skill files for evaluation rubrics, writing style, and LaTeX templates to generate initial drafts.
- Step 3 spawns an isolated reviewer agent that returns structured JSON edits (Part A) and narrative feedback (Part B).
- Step 5 mandates LaTeX compilation (
lualatexfor CVs,xelatexfor cover letters) and ATS verification viapdftotexttext extraction. - All artifacts are validated against the global checklist in
CLAUDE.mdbefore the workflow completes.
Frequently Asked Questions
How does the drafter-reviewer workflow prevent factual errors in generated documents?
The workflow implements a "grounding audit" where the reviewer agent validates every claim in the drafts against the static candidate profile stored in .claude/skills/job-application-assistant/01-candidate-profile.md. Additionally, the drafter must obey "grounding audit" rules during Step 2, ensuring all dates, metrics, and role descriptions match the master CV file (cv/main_example.tex) exactly.
What safety measures protect against malicious content in job postings?
Step 0 explicitly treats the posting as untrusted data by parsing only structural metadata (company, role, location) and refusing to follow any URLs, instructions, or hidden directives embedded within the text. The reviewer agent in Step 3 receives only the company name and pre-approved reference files, completely isolating it from the raw posting content.
Why does the workflow use different LaTeX engines for CVs and cover letters?
The pipeline uses lualatex for CV compilation and xelatex for cover letters (as specified in .claude/commands/apply.md) to handle specific font rendering requirements and layout constraints defined in the template files. This dual-engine approach ensures proper Unicode support and microtypography while respecting the page-limit enforcement rules in .claude/skills/job-application-assistant/05-cv-templates.md.
How are reviewer suggestions actually applied to the documents?
The reviewer returns Part A feedback as machine-readable JSON objects specifying exact old_string and new_string pairs for the Edit tool, allowing the drafter to make precise file modifications without re-reading the entire draft. Part B suggestions (narrative improvements regarding tone or company angles) are addressed manually by the drafter in Step 4 before proceeding to compilation.
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 →