From 32e63e5143e6cad8ad932c67eb942d20b7d10b04 Mon Sep 17 00:00:00 2001 From: Mads Lorentzen Date: Fri, 24 Apr 2026 19:34:46 +0200 Subject: [PATCH 1/5] feat(apply): add mandatory PDF compile-and-inspect step and relevance-weighted cutting Adds a new Step 5 to the /apply workflow that compiles the CV (lualatex) and cover letter (xelatex) and visually inspects the resulting PDFs before presenting them to the user. This catches page-break failures that .tex review cannot detect: orphaned cventry titles, cover letters spilling to a second page, and bullet fonts not matching body text. Also introduces relevance-weighted cutting as the rule for shrinking an overflowing CV: score each candidate line by (a) relevance to the posting, (b) uniqueness in the document, and (c) load-bearing role in the cover letter, then cut the lowest-total-score line first, regardless of section. This replaces the old static "cut oldest section first" heuristic, which was wrong whenever a relevant older-role bullet competed with an irrelevant recent-role bullet. Bundled with the earlier pending token-efficiency improvements to the /apply workflow (inline draft passing to the reviewer, scoped reviewer file reads, single end-of-workflow verification pass). Changes: - .claude/commands/apply.md: new Step 5 (Compile & Inspect PDFs), renumber old Step 5 to Step 6, update token-efficiency rules at top to reference Step 6 for the verification checklist - .claude/skills/job-application-assistant/05-cv-templates.md: switch recommended compile engine from pdflatex to lualatex (pdflatex fails on modern MiKTeX with fontawesome5 font-expansion errors), add "Compile-and-Inspect Loop" section with needspace/enlargethispage patterns, add "Relevance-weighted cutting" section - .claude/skills/job-application-assistant/06-cover-letter-templates.md: add compile command, add "Compile-and-Inspect Loop", document the "\lettercontent{} + itemize" pitfall and the required font-matching Raleway-Medium wrapper fix - CLAUDE.md: add "Compiled PDF verification (MANDATORY)" checklist items Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/commands/apply.md | 164 +++++++++++------- .../05-cv-templates.md | 65 ++++++- .../06-cover-letter-templates.md | 47 ++++- CLAUDE.md | 8 + 4 files changed, 223 insertions(+), 61 deletions(-) diff --git a/.claude/commands/apply.md b/.claude/commands/apply.md index bc46560..664248f 100644 --- a/.claude/commands/apply.md +++ b/.claude/commands/apply.md @@ -4,6 +4,12 @@ You are orchestrating a two-agent job application workflow. The job posting is p Follow these steps **exactly in order**. Do not skip steps. +**Token-efficiency rules for this workflow:** +- Never re-Read a file whose contents are already in your context from an earlier step. If you read it in Step 1, it is still available in Step 2. +- When dispatching the reviewer agent, pass draft content **inline in the agent prompt** rather than asking the agent to Read files you already have in memory. +- Run the full verification checklist exactly once, at the end (Step 6). The reviewer focuses on content critique, not verification. +- Step 5 (compile and inspect PDFs) is mandatory and non-skippable — LaTeX page-break decisions are unpredictable, and `.tex` files that look fine often produce broken PDFs (orphaned entry titles, cover letters spilling to page 2, bullet fonts mismatching). + --- ## Step 0: Parse Input @@ -46,13 +52,14 @@ After presenting the evaluation, ask the user: ## Step 2: DRAFTER - Draft CV + Cover Letter -Read the following reference files: -- `.claude/skills/job-application-assistant/01-candidate-profile.md` +You already have `01-candidate-profile.md` and `04-job-evaluation.md` in context from Step 1. **Do not re-read them.** + +Read only the reference files you do not yet have: - `.claude/skills/job-application-assistant/03-writing-style.md` - `.claude/skills/job-application-assistant/05-cv-templates.md` - `.claude/skills/job-application-assistant/06-cover-letter-templates.md` -Also read the most recent existing CV and cover letter files for structural reference: +Also read the most recent existing CV and cover letter files for concrete structural reference (one of each is enough): - Read any existing `cv/main_*.tex` file as a LaTeX template reference - Read any existing `cover_letters/cover_*.tex` or `cover_letters/Cover_*.tex` file as a template reference @@ -72,13 +79,15 @@ Also read the most recent existing CV and cover letter files for structural refe - Keep to approximately one page - Any mention of agentic coding or AI tooling must reference **Claude Code** by name -Write both files to disk. +Write both files to disk. Keep the exact text of both drafts in working memory — you will pass them inline to the reviewer in Step 3 and revise them in Step 4 without re-reading. --- ## Step 3: REVIEWER - Research & Critique -Use the **Agent tool** to spawn a `general-purpose` reviewer agent with the following prompt: +Use the **Agent tool** to spawn a `general-purpose` reviewer agent. The reviewer gets a fresh context, so pass the drafts **inline in the prompt** below (do not make the reviewer Read them). Scope the reviewer's file reads to content-critique essentials only — the reviewer does not need the LaTeX template files (`05`, `06`) or the behavioral profile (`02`) to critique content. + +Replace ``, ``, ``, ``, and `` with actual values before dispatching. ``` You are a hiring manager proxy reviewing a job application. Your job is to make the application as targeted and compelling as possible. @@ -92,63 +101,58 @@ Use WebSearch and WebFetch to research: - Any recent projects, press releases, or strategic initiatives relevant to the role - Company culture and values -### 2. Read All Reference Materials -Read these files to understand the candidate and quality standards: +### 2. Read Reference Materials (content-critique only) +Read these three files — and only these — to ground your critique: - `.claude/skills/job-application-assistant/01-candidate-profile.md` -- `.claude/skills/job-application-assistant/02-behavioral-profile.md` - `.claude/skills/job-application-assistant/03-writing-style.md` - `.claude/skills/job-application-assistant/04-job-evaluation.md` -- `.claude/skills/job-application-assistant/05-cv-templates.md` -- `.claude/skills/job-application-assistant/06-cover-letter-templates.md` -### 3. Read the Drafts -Read the drafted CV and cover letter: -- `cv/main_.tex` -- `cover_letters/cover__.tex` +Do NOT read `02-behavioral-profile.md`, `05-cv-templates.md`, or `06-cover-letter-templates.md` — those are structural/behavioral references the drafter already applied and are not needed for content critique. -### 4. Read the Job Posting +### 3. Drafts to Review +Both drafts are provided inline below. Do NOT use the Read tool on the draft files — use these exact texts. + + + + + + + + + +### 4. Job Posting ### 5. Produce Feedback -Return a structured critique with **specific, actionable suggestions** in these categories: -**a) Missed keywords/requirements** -- List any requirements or keywords from the posting that are not addressed in the CV or cover letter -- For each, suggest where and how to add them (with specific text suggestions) +Return your feedback in **two parts**: -**b) Company/department-specific angles** -- Based on your research, suggest specific angles to add -- Suggest how to connect experience to the company's strategic priorities +**Part A — Structured edits (preferred format whenever possible):** +A JSON array of concrete edits the drafter can apply directly without re-reading the files. Each edit is an object: +```json +{ + "file": "cv/main_.tex" | "cover_letters/cover__.tex", + "old_string": "", + "new_string": "", + "reason": "" +} +``` +Only use this format when you can quote the exact `old_string` from the drafts above. Make `old_string` unique — include enough surrounding context so it matches exactly once per file. -**c) Action-oriented reframing** -- Identify passive or generic statements and suggest action-oriented rewrites - -**d) Tone and style issues** -- Check against the writing style guide (03-writing-style.md) -- Flag any issues with tone, formality, or voice - -**e) Verification checklist** -Run this checklist and report pass/fail for each: -- [ ] All claims match actual profile - no fabricated skills, experience, or achievements -- [ ] Job titles, dates, company names, and locations are correct -- [ ] Contact details are correct -- [ ] Profile statement is tailored to the specific role -- [ ] Key job requirements are addressed -- [ ] No LaTeX syntax errors (balanced braces, correct commands) -- [ ] No spelling or grammar errors -- [ ] Agentic coding / AI tooling references mention Claude Code by name -- [ ] Cover letter addressed correctly -- [ ] Cover letter fits approximately one page -- [ ] CV follows 2-page moderncv/banking format +**Part B — Narrative suggestions (for judgment calls that are not mechanical edits):** +Prose suggestions grouped by category: +- **Missed keywords/requirements** — what to add and roughly where, if it cannot be expressed as a clean string replacement +- **Company/department-specific angles** — connections between experience and the company's strategic priorities, based on your research +- **Tone and style issues** — anything that conflicts with `03-writing-style.md` **CRITICAL RULE:** All suggestions must be grounded in actual profile data. Do NOT suggest fabricating skills, experience, or achievements. If a requirement is a gap, say so honestly and suggest how to frame adjacent experience instead. -Return your full feedback as a single structured message. -``` +Do **not** run a verification checklist — the drafter will do that in the final step. Focus on content critique. -**Important:** Before spawning the agent, replace ``, ``, and `` with the actual values from Steps 0-2. +Return Part A and Part B together as a single structured message. +``` --- @@ -156,25 +160,69 @@ Return your full feedback as a single structured message. Once the reviewer agent returns its feedback: -1. Read the reviewer's suggestions carefully -2. Read both draft files again -3. Incorporate the suggestions that improve the application: - - Add missed keywords where they fit naturally - - Add company-specific angles from the reviewer's research - - Reframe passive statements to be more action-oriented - - Fix any tone/style issues - - Fix any verification checklist failures -4. Update both files **in place** (edit, don't recreate) -5. Do NOT incorporate suggestions that would fabricate skills or experience +1. **Apply Part A (structured edits) directly with the Edit tool.** Do NOT re-read the draft files — you already have them in context from Step 2, and the reviewer's `old_string` values were quoted from that same text. For each edit in the JSON array, call `Edit` with the given `file`, `old_string`, and `new_string`. Skip any whose rationale would require fabricating content. +2. **Apply Part B (narrative suggestions)** using judgment — these are the changes that need interpretation (reframing paragraphs, adding company angles, adjusting opening). Use Edit for targeted changes; only re-read a file if an edit fails because the surrounding text has shifted. +3. Do NOT incorporate any suggestion that would fabricate skills or experience. + +After all edits are applied, the two files on disk are the final drafts. --- -## Step 5: Present Final Output +## Step 5: DRAFTER - Compile & Inspect PDFs (MANDATORY) -After revision, present to the user: +**Never skip this step.** The `.tex` files looking fine is not sufficient — LaTeX page-break decisions are unpredictable and commonly produce broken layouts (orphaned job titles separated from their bullets, cover letters spilling to 2 pages, bullet fonts not matching body text). Compile both documents and visually verify the PDFs before presenting. + +### 5a. Compile + +```bash +cd cv && lualatex -interaction=nonstopmode main_.tex +cd ../cover_letters && xelatex -interaction=nonstopmode cover__.tex +``` + +- CV uses **lualatex** — pdflatex fails on modern MiKTeX with fontawesome5 font-expansion errors. lualatex handles the same sources cleanly. +- Cover letter uses **xelatex** — cover.cls requires fontspec. + +If either compile fails, fix the error and re-compile until clean. + +### 5b. Inspect layout + +Read both PDFs via the Read tool and verify: + +**CV (`cv/main_.pdf`):** +- [ ] Exactly 2 pages (not 1, not 3) +- [ ] No orphaned `\cventry` titles — a job/education title line must never sit alone at the bottom of page 1 with its bullets on page 2. This is the most common failure. +- [ ] Section headings are not isolated at the top of page 2 with only 1-2 lines below +- [ ] No awkward whitespace gaps + +**Cover letter (`cover_letters/cover__.pdf`):** +- [ ] Exactly 1 page +- [ ] Signature block visible, not cut off or pushed to a second page +- [ ] Bullet list font matches surrounding body text (both should be Raleway-Medium) + +### 5c. Iterate until clean + +If the layout has problems, edit the `.tex` files and recompile. Common fixes (see `05-cv-templates.md` and `06-cover-letter-templates.md` for full details): + +- **Orphaned CV entry title:** `\usepackage{needspace}` in preamble, then `\needspace{5\baselineskip}` immediately before the problematic `\cventry` +- **CV spills to page 3 with only a trailing section:** `\enlargethispage{2-3\baselineskip}` before a late section +- **Substantial content on page 3:** cut content using **relevance-weighted cutting** (see `05-cv-templates.md` → "Relevance-weighted cutting"). Score each candidate line by (a) relevance to THIS posting's keywords and responsibilities, (b) uniqueness (is it duplicated elsewhere?), (c) narrative load (does the cover letter depend on it?). Cut the lowest-total-score line first, regardless of section. Do NOT mechanically apply a static section-based priority order — an older-role bullet that hits posting keywords is worth more than a recent-role bullet that does not. +- **Cover letter itemize breaks compile or uses wrong font:** close `\lettercontent{}` before the list, wrap the list in `{\raggedright\fontspec[Path = OpenFonts/fonts/raleway/]{Raleway-Medium}\fontsize{11pt}{13pt}\selectfont \begin{itemize}...\end{itemize}\par}` +- **Cover letter spills to 2 pages:** trim using the same relevance-weighted logic. First cut: sentences that restate what a bullet already said. Second cut: a bullet that does not hit posting keywords. Last resort: a bullet that does hit posting keywords. Never reduce geometry or line spacing. + +Do not proceed to Step 6 until both PDFs pass inspection. + +### 5d. Clean up build artifacts + +After the final clean compile, delete the `.aux`, `.log`, `.out` files (keep the `.tex` and `.pdf`). + +--- + +## Step 6: Present Final Output + +Run the full verification checklist from `CLAUDE.md` now — this is the **only** verification pass in the workflow. Re-read both files once here to verify final state on disk matches your mental model after the Step 4 and Step 5 edits. ### Verification Checklist -Re-run the full verification checklist from CLAUDE.md and report pass/fail for each item. +Report pass/fail for each item in the CLAUDE.md verification checklist (factual accuracy, targeting, consistency, quality). ### Key Tailoring Decisions Summarize 3-5 key decisions made to tailor the application: diff --git a/.claude/skills/job-application-assistant/05-cv-templates.md b/.claude/skills/job-application-assistant/05-cv-templates.md index f9aef7b..090a34e 100644 --- a/.claude/skills/job-application-assistant/05-cv-templates.md +++ b/.claude/skills/job-application-assistant/05-cv-templates.md @@ -7,9 +7,17 @@ All CVs use the moderncv LaTeX package with the "banking" style and "blue" color scheme. **Output file:** `cv/main_.tex` -**Compile with:** pdflatex (not xelatex) +**Compile with:** **lualatex** on MiKTeX/TeX Live. pdflatex often fails on modern MiKTeX installs with `fontawesome5` font-expansion errors; lualatex handles the same sources cleanly. **Master reference:** `cv/main_example.tex` (comprehensive CV with all competencies, experience, and achievements - use as source when building targeted CVs) +### Compile command + +```bash +cd cv && lualatex -interaction=nonstopmode main_.tex +``` + +Expected output: `Output written on main_.pdf (2 pages, ...)`. Any page count other than 2 is a failure that must be fixed before presenting to the user. + ## Document Structure ```latex @@ -101,6 +109,34 @@ If there is a gap in your employment history: - End with: "More references are available upon request." - **Do not attach reference letters** - employers typically contact references directly +## Compile-and-Inspect Loop (MANDATORY) + +After writing the CV and before presenting to the user, always compile and visually inspect the PDF. Iterate until the layout is clean. Workflow: + +1. Run `lualatex -interaction=nonstopmode main_.tex` +2. Check the output page count: must be exactly 2 +3. Read the PDF via the Read tool and visually inspect both pages +4. Check for **orphaned entries**: a `\cventry` title line must never sit alone at the bottom of page 1 with its bullets on page 2 + +### Fixing common page-break problems + +**Problem: entry title on page 1, bullets orphaned to page 2** +Add `\needspace{5\baselineskip}` immediately before the problematic `\cventry`: +```latex +\needspace{5\baselineskip} +\item{\cventry{YEAR--YEAR}{Role Title}{Organization}{Location}{}{...}} +``` +Include `\usepackage{needspace}` in the preamble. + +**Problem: one trailing section spills to page 3 (e.g., References alone on page 3)** +Add `\enlargethispage{2-3\baselineskip}` before a late section (e.g., before `\section{Honors and Awards}`) to stretch page 2 by a few lines. This is the standard LaTeX rescue for near-miss overflows. + +**Problem: 3 pages with significant content on page 3** +Cut content — do not compress geometry or `\vspace`. See "Relevance-weighted cutting" below for the rule. + +**Problem: content finishes early on page 2 (feels thin)** +Restore the highest-relevance item that was previously cut — a CV that ends mid-page 2 looks incomplete. + ## Page Budget - Hard 2-Page Limit The CV **must** fit on exactly 2 pages when compiled. Use these content limits as a guide: @@ -119,6 +155,33 @@ The CV **must** fit on exactly 2 pages when compiled. Use these content limits a **If in doubt, cut rather than squeeze.** Reducing `\vspace` or geometry scale to force-fit content makes the CV look cramped. +## Relevance-weighted cutting (the right way to shrink a CV) + +**Cut by signal, not by section.** Static priority lists ("remove oldest education first, then shorten the earliest role...") are wrong when a relevant "lower-priority" item is competing with an irrelevant "higher-priority" item. An older-role bullet that speaks directly to the posting is worth more than a recent-role bullet that does not. + +For every candidate line, score three things: + +1. **Relevance to THIS posting** — does the line hit a named tool, keyword, or stated responsibility in the job ad? +2. **Uniqueness** — is it the only place this claim appears, or is it duplicated elsewhere in the CV? +3. **Narrative load** — does the cover letter depend on it? If cutting the line would force you to rewrite a cover-letter paragraph, it is load-bearing. + +Cut the lowest-total-score line first, regardless of which section it sits in. + +### Practical order of cuts (easiest → last resort) + +1. **Redundancy.** If an achievement appears in both Core Competencies AND a role bullet, the Core Competencies version is usually the cleaner cut (the experience bullet is more concrete evidence). +2. **Profile-statement fluff.** A sentence that just restates what Publications or Skills will show. ("Peer-reviewed publications on X..." is already a Publications entry — profile can claim it once and stop.) +3. **Low-relevance experience bullets.** A bullet about work that does not touch posting keywords, wherever it sits. This cuts across sections before touching the structural list. +4. **Low-relevance supporting content.** An older-role bullet that does not speak to the target role. A certification that does not touch the posting's stack. A language entry that can be condensed to one line. +5. **Low-relevance publications.** Keep 1-2 publications that best match the posting. Cut the rest before touching experience bullets. +6. **Last-resort structural cuts.** Oldest education entry, tightening an older role to 2 bullets, collapsing Certifications into a single line. These only happen if the relevance-weighted cuts above have already been exhausted. + +### Pitfalls to avoid + +- Do not mechanically cut from the bottom of a static section list without checking relevance. "Cut the oldest role first" is wrong if that role is literally about the skill the posting asks for. +- Do not cut the one concrete example the cover letter leans on. Relevance is measured against the cover letter you wrote, not just the job posting — interviewers will have read both. +- Do not cut to fit if the fit is borderline (2.02 pages). Prefer `\enlargethispage{2-3\baselineskip}` on a late section for near-misses; reserve content cuts for genuine overflow (content on page 3 that is more than a single trailing section). + ## Recommended Section Order The section order varies by role type: diff --git a/.claude/skills/job-application-assistant/06-cover-letter-templates.md b/.claude/skills/job-application-assistant/06-cover-letter-templates.md index 9bea2d0..293ba95 100644 --- a/.claude/skills/job-application-assistant/06-cover-letter-templates.md +++ b/.claude/skills/job-application-assistant/06-cover-letter-templates.md @@ -5,9 +5,52 @@ Cover letters use a custom LaTeX document class (`cover.cls`) with Lato/Raleway fonts. **Output file:** `cover_letters/cover__.tex` -**Compile with:** XeLaTeX (not pdflatex) +**Compile with:** XeLaTeX (cover.cls requires fontspec) **Font directory:** `cover_letters/OpenFonts/fonts/` +### Compile command + +```bash +cd cover_letters && xelatex -interaction=nonstopmode cover__.tex +``` + +Expected output: `Output written on cover__.pdf (1 page, ...)`. Any page count other than 1 is a failure that must be fixed before presenting to the user. + +## Compile-and-Inspect Loop (MANDATORY) + +After writing the cover letter and before presenting to the user, always compile and visually inspect the PDF. Iterate until the layout is clean: + +1. Run `xelatex -interaction=nonstopmode cover__.tex` +2. Confirm page count is exactly 1 and compile succeeded +3. Read the PDF via the Read tool and visually check: signature fits at the bottom, no text cut off, bullet font matches body + +### Known template pitfall: itemize inside `\lettercontent{}` + +The `\lettercontent{}` macro appends `\\` to its argument. This breaks when the argument ends in `\end{itemize}` because `\\` has no line to break after the environment closes, producing `! LaTeX Error: There's no line here to end.` and no PDF output. + +**Wrong (breaks compile):** +```latex +\lettercontent{Here is how my experience maps: +\begin{itemize} + \item ... +\end{itemize}} +``` + +**Correct — close `\lettercontent{}` before the list and wrap the list in the matching Raleway-Medium font so typography stays consistent:** +```latex +\lettercontent{Here is how my experience maps:} + +{\raggedright\fontspec[Path = OpenFonts/fonts/raleway/]{Raleway-Medium}\fontsize{11pt}{13pt}\selectfont +\begin{itemize} + \item ... +\end{itemize}\par} +\vspace{6pt} + +\lettercontent{[next paragraph]} +``` + +The font wrapper is mandatory — if you just move `\begin{itemize}` outside `\lettercontent{}` without the `\fontspec` block, bullets render in the default body font (Lato) and visually mismatch the rest of the letter. + ## Document Structure ```latex @@ -92,7 +135,7 @@ Cover letters use a custom LaTeX document class (`cover.cls`) with Lato/Raleway - Use `\vspace{.5cm}` between major sections for readability (only if space permits) ### Bullet Lists -- Use `\begin{itemize}...\end{itemize}` inside a `\lettercontent{}` block +- Place `\begin{itemize}...\end{itemize}` **outside** a `\lettercontent{}` block (see "Known template pitfall" above), wrapped in the matching Raleway-Medium `\fontspec` so the bullet font matches the body - 3-5 bullets is ideal - Start each bullet with bold label or action verb - Use `\textbf{Label:}` for category-style bullets diff --git a/CLAUDE.md b/CLAUDE.md index f61885f..773a798 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -118,3 +118,11 @@ After creating or updating a CV or cover letter, re-read the generated file and - [ ] Agentic coding / AI tooling references mention **Claude Code** by name - [ ] Cover letter is addressed to the correct person (or "Dear Hiring Manager" if unknown) - [ ] Cover letter fits approximately one page + +### Compiled PDF verification (MANDATORY - never skip) +Both documents MUST be compiled and visually inspected via the Read tool on the PDF output. "Looks fine in the .tex" is not acceptable - LaTeX page-break decisions are unpredictable. Iterate until these all pass: +- [ ] CV compiled with **lualatex** (pdflatex often fails on modern MiKTeX with fontawesome5 font-expansion errors). Cover letter compiled with **xelatex** (cover.cls requires fontspec). +- [ ] **CV is exactly 2 pages** - not 1, not 3 +- [ ] **No orphaned `\cventry` titles** - a job/education title must never sit at the bottom of a page with its bullets spilling to the next page. Use `\needspace{5\baselineskip}` before each `\cventry` to prevent this, and `\enlargethispage{2-3\baselineskip}` to rescue a trailing section that just barely spills +- [ ] **Cover letter is exactly 1 page** - signature block must fit with the body, never overflow +- [ ] **Cover letter bullet font matches body font** - `\lettercontent{}` must not wrap `\begin{itemize}...\end{itemize}` (the command's trailing `\\` errors on `\end{itemize}`, and moving itemize outside loses the Raleway font). Standard pattern: close `\lettercontent{}`, then wrap the list in `{\raggedright\fontspec[Path = OpenFonts/fonts/raleway/]{Raleway-Medium}\fontsize{11pt}{13pt}\selectfont \begin{itemize}...\end{itemize}\par}` From 00678f9669255f5cbc48bf40b8165070aeb16418 Mon Sep 17 00:00:00 2001 From: Mads Lorentzen Date: Fri, 24 Apr 2026 19:36:45 +0200 Subject: [PATCH 2/5] docs: document PDF verification loop and relevance-weighted cutting Updates README.md and SETUP.md to reflect the new /apply workflow additions shipped in the previous commit: - Bumps the /apply step list from 6 to 7, adding "Compile and inspect" between Revise and Present - Adds a "What makes this workflow different" subsection highlighting the PDF verification loop, relevance-weighted CV cutting, drafter-reviewer separation, and token-efficient dispatching - Updates prerequisites note to call out lualatex (CV) and xelatex (cover letter) explicitly, with the reason each engine is required - Updates SETUP.md's LaTeX section to match (pdflatex -> lualatex for the CV, with the MiKTeX fontawesome5 caveat) Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 14 +++++++++++--- SETUP.md | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3e4908d..c9dbab9 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ The framework encodes career guidance best practices, including structured evalu - [Claude Code](https://claude.com/claude-code) (CLI) - Python 3.10+ - [Bun](https://bun.sh) (for Danish job search CLI tools) -- LaTeX distribution (for compiling CVs and cover letters): [TeX Live](https://tug.org/texlive/) or [MiKTeX](https://miktex.org/) +- 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`. ## Quick start @@ -127,17 +127,25 @@ ai-job-search/ ## How `/apply` works -The `/apply` command runs a **drafter-reviewer workflow**: +The `/apply` command runs a **drafter-reviewer workflow** with mandatory PDF compilation: 1. **Parse** the job posting (URL or text) 2. **Evaluate fit** against your profile (skills, experience, culture, location, career alignment) 3. **Draft** a tailored CV and cover letter in LaTeX 4. **Spawn a reviewer agent** that researches the company and critiques the drafts 5. **Revise** based on the reviewer's feedback -6. **Present** the final output with a verification checklist +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 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. +- **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 receives drafts inline rather than re-reading files. Verification runs once, at the end. + ## Customization ### Which files to edit manually diff --git a/SETUP.md b/SETUP.md index c3bb4fd..029e0eb 100644 --- a/SETUP.md +++ b/SETUP.md @@ -38,7 +38,7 @@ Install a LaTeX distribution to compile the generated `.tex` files to PDF: - **macOS:** [MacTeX](https://tug.org/mactex/) - **Linux:** `sudo apt install texlive-full` or `sudo dnf install texlive-scheme-full` -The CV compiles with `pdflatex`. The cover letter compiles with `xelatex` (for custom fonts). +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` for its custom Lato/Raleway fonts. ## 2. Fork and clone From 906ac1bc19310334e30f2e8994360aee06bb7b1f Mon Sep 17 00:00:00 2001 From: Mads Lorentzen Date: Fri, 24 Apr 2026 20:12:39 +0200 Subject: [PATCH 3/5] fix(apply): restore QC coverage dropped during earlier token-efficiency refactor Audit of master vs the branch found three real coverage gaps in the reviewer prompt and drafter's revise step. This commit restores them without giving up the Part A (JSON edits) + Part B (narrative) structure. 1. Action-oriented reframing is restored as a Part B narrative category. The previous refactor folded reframing into Part A edit rationales, but structural weakness (e.g. "the whole opening paragraph is passive") does not fit a clean find-and-replace and needs prose critique to surface. 2. Tone and style phrasing is strengthened from "anything that conflicts with 03-writing-style.md" back to the master's proactive "check against... flag issues with tone, formality, or voice", with explicit examples (cliches, hedging, over-humility, inconsistent register). 3. Step 4 revise guidance is expanded from "Apply Part A... Apply Part B using judgment" (which could be read as a free-for-all) to an enumerated sub-list under Part B covering every category the reviewer returns: missed keywords, company angles, action-oriented reframing, tone/style. Keeps the architectural change (Part A/B split) but restores the master's explicit "apply every kind of fix" discipline. Also adds a small instruction to the reviewer: produce each Part B category even if the finding is "no issues" - silence on a category can be mistaken for skipping it. One remaining judgment call left unchanged in this commit: the reviewer no longer reads 02-behavioral-profile.md. The refactor excluded it on the reasoning that 03-writing-style.md carries the voice/tone rules. This is a conscious trade-off; if voice-matching starts slipping in practice, restoring 02 to the reviewer's reading list is a one-line fix. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/commands/apply.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.claude/commands/apply.md b/.claude/commands/apply.md index 664248f..9350f53 100644 --- a/.claude/commands/apply.md +++ b/.claude/commands/apply.md @@ -142,10 +142,11 @@ A JSON array of concrete edits the drafter can apply directly without re-reading Only use this format when you can quote the exact `old_string` from the drafts above. Make `old_string` unique — include enough surrounding context so it matches exactly once per file. **Part B — Narrative suggestions (for judgment calls that are not mechanical edits):** -Prose suggestions grouped by category: +Prose suggestions grouped by category. Produce each category even if your finding is "no issues" — silence on a category can be mistaken for skipping it. - **Missed keywords/requirements** — what to add and roughly where, if it cannot be expressed as a clean string replacement - **Company/department-specific angles** — connections between experience and the company's strategic priorities, based on your research -- **Tone and style issues** — anything that conflicts with `03-writing-style.md` +- **Action-oriented reframing** — identify passive, generic, or low-energy statements and suggest action-oriented rewrites. Use this category especially for structural weakness that doesn't fit a single-sentence swap (e.g., "the whole opening paragraph reads as passive — restructure around your single strongest match to the posting"). +- **Tone and style issues** — check against `03-writing-style.md`. Flag any issues with tone, formality, or voice (cliches, hedging, over-humility, inconsistent register). **CRITICAL RULE:** All suggestions must be grounded in actual profile data. Do NOT suggest fabricating skills, experience, or achievements. If a requirement is a gap, say so honestly and suggest how to frame adjacent experience instead. @@ -161,8 +162,13 @@ Return Part A and Part B together as a single structured message. Once the reviewer agent returns its feedback: 1. **Apply Part A (structured edits) directly with the Edit tool.** Do NOT re-read the draft files — you already have them in context from Step 2, and the reviewer's `old_string` values were quoted from that same text. For each edit in the JSON array, call `Edit` with the given `file`, `old_string`, and `new_string`. Skip any whose rationale would require fabricating content. -2. **Apply Part B (narrative suggestions)** using judgment — these are the changes that need interpretation (reframing paragraphs, adding company angles, adjusting opening). Use Edit for targeted changes; only re-read a file if an edit fails because the surrounding text has shifted. -3. Do NOT incorporate any suggestion that would fabricate skills or experience. +2. **Apply Part B (narrative suggestions)** using judgment. These need interpretation, not mechanical replacement. Walk through every Part B category the reviewer returned and address it: + - **Missed keywords/requirements:** add the keyword or capability where it fits naturally in the CV or cover letter. Prefer the experience bullets (concrete evidence) over the profile statement (abstract claim). + - **Company/department-specific angles:** weave the reviewer's research into the cover letter opening or motivation paragraph. Verify every company claim via WebFetch/WebSearch before including it — do not trust reviewer research at face value. + - **Action-oriented reframing:** rewrite passive or generic phrasing (CV profile statement, cover letter opening, bullet leads). Structural weakness that the reviewer flagged without a clean JSON edit lives here. + - **Tone and style issues:** apply the writing-style-guide fixes (no em-dashes, no cliches, no apologetic hedging, consistent first-person active voice). + Use Edit for targeted changes; only re-read a file if an edit fails because the surrounding text has shifted. +3. Do NOT incorporate any suggestion that would fabricate skills or experience. If a posting requirement is a genuine gap, acknowledge it honestly and frame adjacent experience instead. After all edits are applied, the two files on disk are the final drafts. From fa9c422d261adbe0e89635456ad81eb47353e151 Mon Sep 17 00:00:00 2001 From: Mads Lorentzen Date: Fri, 24 Apr 2026 20:14:59 +0200 Subject: [PATCH 4/5] feat(apply): restore 02-behavioral-profile.md to reviewer reading list Brings 02-behavioral-profile.md back into the reviewer's reading list (it was dropped in the earlier token-efficiency refactor). The reviewer now reads four files: 01 (candidate profile), 02 (behavioral profile), 03 (writing style), 04 (job evaluation). The LaTeX template files (05, 06) remain excluded, since those govern structural/LaTeX concerns the drafter already applied. The behavioral profile is there to let the reviewer cross-check whether the cover letter's voice matches the candidate's natural register - something 03-writing-style.md does not encode (03 has the universal rules like "no em-dashes, no cliches"; 02 has the per-person register like "Collaborator PI should not be given a combative solo-hero tone"). Also cross-references 02 explicitly in the Part B "Tone and style issues" category so the reviewer actually uses it during critique rather than just skimming it. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/commands/apply.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.claude/commands/apply.md b/.claude/commands/apply.md index 9350f53..618069a 100644 --- a/.claude/commands/apply.md +++ b/.claude/commands/apply.md @@ -85,7 +85,7 @@ Write both files to disk. Keep the exact text of both drafts in working memory ## Step 3: REVIEWER - Research & Critique -Use the **Agent tool** to spawn a `general-purpose` reviewer agent. The reviewer gets a fresh context, so pass the drafts **inline in the prompt** below (do not make the reviewer Read them). Scope the reviewer's file reads to content-critique essentials only — the reviewer does not need the LaTeX template files (`05`, `06`) or the behavioral profile (`02`) to critique content. +Use the **Agent tool** to spawn a `general-purpose` reviewer agent. The reviewer gets a fresh context, so pass the drafts **inline in the prompt** below (do not make the reviewer Read them). Scope the reviewer's file reads to content-critique essentials only — the reviewer does not need the LaTeX template files (`05`, `06`) to critique content, since those govern structural/LaTeX concerns the drafter already applied. Replace ``, ``, ``, ``, and `` with actual values before dispatching. @@ -102,12 +102,13 @@ Use WebSearch and WebFetch to research: - Company culture and values ### 2. Read Reference Materials (content-critique only) -Read these three files — and only these — to ground your critique: +Read these four files — and only these — to ground your critique: - `.claude/skills/job-application-assistant/01-candidate-profile.md` +- `.claude/skills/job-application-assistant/02-behavioral-profile.md` — use this specifically to check whether the cover letter's voice matches the candidate's natural register. A "Collaborator" PI profile, for example, should not be given a combative, solo-hero tone; a "Persuader" profile should not be given over-hedged, apologetic phrasing. - `.claude/skills/job-application-assistant/03-writing-style.md` - `.claude/skills/job-application-assistant/04-job-evaluation.md` -Do NOT read `02-behavioral-profile.md`, `05-cv-templates.md`, or `06-cover-letter-templates.md` — those are structural/behavioral references the drafter already applied and are not needed for content critique. +Do NOT read `05-cv-templates.md` or `06-cover-letter-templates.md` — those govern LaTeX structure the drafter already applied and are not needed for content critique. ### 3. Drafts to Review Both drafts are provided inline below. Do NOT use the Read tool on the draft files — use these exact texts. @@ -146,7 +147,7 @@ Prose suggestions grouped by category. Produce each category even if your findin - **Missed keywords/requirements** — what to add and roughly where, if it cannot be expressed as a clean string replacement - **Company/department-specific angles** — connections between experience and the company's strategic priorities, based on your research - **Action-oriented reframing** — identify passive, generic, or low-energy statements and suggest action-oriented rewrites. Use this category especially for structural weakness that doesn't fit a single-sentence swap (e.g., "the whole opening paragraph reads as passive — restructure around your single strongest match to the posting"). -- **Tone and style issues** — check against `03-writing-style.md`. Flag any issues with tone, formality, or voice (cliches, hedging, over-humility, inconsistent register). +- **Tone and style issues** — check against `03-writing-style.md` AND `02-behavioral-profile.md`. Flag any issues with tone, formality, or voice (cliches, hedging, over-humility, inconsistent register), and specifically flag any mismatch between the letter's voice and the candidate's natural register as described in the behavioral profile. **CRITICAL RULE:** All suggestions must be grounded in actual profile data. Do NOT suggest fabricating skills, experience, or achievements. If a requirement is a gap, say so honestly and suggest how to frame adjacent experience instead. From f62b9ec893ffc1c7fc849d2c2957e5901eb9ac96 Mon Sep 17 00:00:00 2001 From: Mads Lorentzen Date: Sat, 25 Apr 2026 13:56:41 +0200 Subject: [PATCH 5/5] docs(README): soften "token-efficient" bullet to match reality The original bullet ("Token-efficient. Reviewer receives drafts inline rather than re-reading files. Verification runs once, at the end.") is technically true for the reviewer-dispatch path but invites readers to infer that the overall workflow uses fewer tokens than master. That is not actually defensible: the new Step 5 (compile-and-inspect) spends tokens on Read'ing both PDFs and on layout-fix iteration cycles, which typically offsets the savings from inline draft passing and scoped reviewer reads. End-to-end the workflow is roughly token-neutral, with the variance dominated by how many layout fixes the CV needs. Reframes the bullet as "Token-efficient reviewer dispatch" (which is accurate and verifiable from the prompt structure) and adds an explicit note that Step 5 spends some of those savings on PDF rendering. The trade is real - tokens for fewer broken PDFs reaching the user - and worth surfacing honestly rather than overclaiming. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c9dbab9..9b02f8f 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ All claims in the CV and cover letter are verified against your actual profile. - **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. - **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 receives drafts inline rather than re-reading files. Verification runs once, at the end. +- **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. ## Customization