AI Job Search /apply Command Workflow: The 7-Step Drafter-Reviewer Pipeline

The /apply command executes a deterministic seven-step pipeline that archives job postings, generates ATS-compatible PDFs from LaTeX templates, performs company research and optional salary benchmarking, validates text-layer parseability, and records the complete application in a CSV tracker.

The /apply command serves as the core automation engine in the MadsLorentzen/ai-job-search repository, transforming raw job URLs or pasted text into complete, version-controlled application packages. Defined in .claude/commands/apply.md, this workflow implements a safety-first drafter-reviewer architecture where every generated artifact passes through mandatory verification loops before being committed to the central tracker.

The /apply Command Workflow Architecture

The workflow follows a strictly sequenced pipeline from Step 0 through Step 6b, with each phase responsible for deterministic file operations and validation gates. The canonical specification resides in .claude/commands/apply.md, while implementation details are distributed across setup scripts, security tools, and test suites.

Step 0: Archive the Job Posting

The pipeline begins by creating an immutable record of the source material. The command writes the raw posting text verbatim to documents/postings/job_posting.md (or a user-specified archive folder), ensuring the original source remains available for integrity verification by the /outcome command and human reviewers.

Step 1: Draft the Application Documents

In this phase, the system populates CV and cover-letter templates with candidate-specific data. It reads placeholder definitions from 05-cv-templates.md and 06-cover-letter-templates.md, injecting personal details while preserving the template files for version control. This separation of data and presentation ensures reproducible builds.

Step 2: Compile LaTeX PDFs with ATS Verification

The command enters a mandatory compilation loop that generates candidate.pdf and cover.pdf. If pdftotext from the poppler package is installed—as detailed in SETUP.md lines 146-152—the workflow extracts the PDF’s text layer to verify ATS compatibility immediately. Missing dependencies trigger a graceful fallback to visual keyword review rather than blocking the pipeline.

Step 3: Execute Company Research

A dedicated reviewer agent enriches the application with company background data. The system first queries cached research from the company_research/ directory; if cache misses occur, it performs live lookups. Results populate both the final application package and tracker notes, with behavior validated by tests/test_company_research_cache.py.

Step 4: Benchmark Salary Data (Optional)

This gate compares offered compensation against the user’s historical salary data using salary_lookup.py. If salary_data.json is absent, the step skips gracefully per tools/README_SALARY_TOOL.md, allowing the workflow to proceed without personal financial records.

Step 5: Validate ATS Parseability

Before human review, the command enforces text-layer integrity through tools/security_guards.py (lines 60-63). This security guard extracts the PDF text layer to verify that required contact details and keywords are actually present, not merely rendered as images. Failed checks trigger a warning and present a visual review panel rather than submitting unparseable documents.

Step 6a: Human Review and Edit

The workflow pauses for human intervention, presenting the compiled PDFs for fine-tuning. Users can adjust layouts, add missing details, or correct formatting quirks. The compilation loop repeats until the candidate confirms the documents meet their standards, creating a mandatory quality gate.

Step 6b: Record Application in Tracker

Upon approval, the command appends a complete row to job_search_tracker.csv, including columns for status, deadline, company name, role, salary benchmark, and file paths to the generated PDFs. This schema is strictly defined in .claude/commands/apply.md and consumed by downstream commands like /outcome and /html-report. The recording logic is guarded by unit tests in tests/test_apply_records_application.py.

Practical Usage Examples

Invoke the /apply command through Claude Code with flexible input methods:


# Fetch a posting from a supported job portal

/apply https://jobindex.dk/job/1234567

# Pipe raw posting text directly (the command detects non-URL input)

echo "$POSTING_TEXT" | /apply

# Override the default template for a specific application

APP_TEMPLATE=my-moderncv /apply https://example.com/job/98765

All invocations trigger the full drafter-reviewer pipeline, ensuring consistent output regardless of input method.

Summary

The /apply command workflow in AI Job Search provides a deterministic, safety-critical pipeline for job application automation:

  • Immutable archiving preserves original postings in documents/postings/job_posting.md
  • Template-driven generation separates content from presentation using markdown-based CV and cover-letter templates
  • ATS-first verification ensures text-layer parseability through pdftotext integration and security_guards.py validation
  • Optional intelligence layers include cached company research and salary benchmarking with graceful degradation
  • Mandatory human review creates a quality gate before finalizing documents
  • Structured tracking writes standardized records to job_search_tracker.csv for downstream analytics

Frequently Asked Questions

What happens if pdftotext is not installed on my system?

The workflow implements graceful degradation. If the poppler utility pdftotext is unavailable, the command skips the automated text-layer extraction described in SETUP.md and falls back to a visual keyword review panel. This ensures the pipeline completes even without the optional ATS-verification dependency, though manual verification becomes necessary.

How does the salary benchmarking step handle missing data?

Step 4 is entirely optional. The system attempts to execute salary_lookup.py against salary_data.json, but if the salary data file is absent, the step skips automatically without blocking the workflow. The application proceeds to record in the tracker, simply leaving the salary benchmark column empty or marked as unavailable.

Where does the /apply command store application records?

All applications are recorded in job_search_tracker.csv at the repository root. This file maintains a strict column schema defined in .claude/commands/apply.md that includes application status, deadlines, company details, role titles, salary benchmarks, and absolute paths to the generated candidate.pdf and cover.pdf files. The schema ensures compatibility with the /outcome and /html-report commands.

Can I customize the CV and cover letter templates used by /apply?

Yes. Templates are managed through the /add-template command and stored in 05-cv-templates.md and 06-cover-letter-templates.md. You can override the default template for any single invocation by setting the APP_TEMPLATE environment variable, as shown in the usage examples. The workflow reads these markdown templates and injects candidate data while keeping template definitions under version control.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →