Skill

resume-tailoring

careerai-agentspattern
Trigger

User needs to tailor a resume for a specific job posting, optimizing for ATS and recruiter scanning

Version: 260428

Changelog

260428: shadow-mode gate + outcome feedback

  • Replaced disabled re-scoring gate with shadow-mode specification (logs delta to JSONL, no blocking)
  • Exit criteria: 50 data points → median delta + outcome correlation analysis
  • Phase 1A of vault audit remediation (Finding 1)
  • Added Outcome Feedback section reading from ~/vault/meta/outcome-feedback.jsonl (Phase 3C)

260420: multiple edits

  • v_migrate: Changelog migrated from table to YYMMDD H3 format per versioning-standard rule 2 (V1.6 of skills upgrade plan)
  • v6: Added license, sources per V6.1/V6.2 of skills upgrade plan.
  • v1.5: Added ## Quality Checks section per V1.5 of ~/vault/plans/2026-04-20-vault-skills-upgrade-plan.md

260403: Added Visual Enrichment section + self-improving-agent-patterns cross-reference

260331: Initial creation


Description

AI-powered resume customization that rewrites a candidate’s resume for each individual job application. The system takes structured resume data (JSON) and a target job posting, then uses an LLM to reorder sections, inject keywords from the job description, quantify achievements, and optimize formatting for Applicant Tracking Systems: all while preserving factual accuracy. No experience is fabricated; the LLM only rephrases, reorders, and emphasizes existing content.

The tailoring pipeline is one of five stages in the jobs-apply unified pipeline (DISCOVER -> MATCH -> TAILOR -> SUBMIT -> TRACK). A job that scores 70+ on J-Score v2 gets tailored in the same cycle it is discovered: no batching. The tailor stage produces both a tailored resume and a personalized cover letter, then renders them to PDF via Puppeteer before the submit stage takes over.

Pattern

  1. Input assembly. buildTailorPrompt() serializes resume_json (experience, education, skills, projects, certifications) and target job (title, company, location, description capped at 2000 chars, requirements). Contact info excluded from tailored output: renderer adds it from profile.

  2. LLM rewrite. ResumeTailor calls OpenRouterProvider.generateJSON() with system prompt (expert resume writer, seven guidelines: keyword match, quantified achievements, action verbs, ATS optimization, truthfulness, relevance ordering, concise bullets), temp 0.4, max 8192 tokens. Returns { content: string, modifications: string[] }.

  3. Cover letter. In parallel, CoverLetterGenerator produces a 3-4 paragraph letter at temp 0.6. Returns { content: string, keyPoints: string[] }. See skills/cover-letter-generation.

  4. PDF rendering. ResumeRenderer converts to ATS-friendly HTML (semantic sections, Georgia/11pt/Letter/0.5in margins) and launches headless Puppeteer. Output: data/resumes/resume_{jobId}_{timestamp}.pdf.

  5. Status flow. Job: matched -> tailoring -> queued. On failure: rolls back to matched. Duplicate check before tailoring (prevents re-PDF generation on retry). Platform batching: Promise.allSettled across platforms, sequential within each platform batch. Cost: ~$0.002/pair at DeepSeek v3 pricing.

Interface

Trigger

The tailor stage activates automatically when a job reaches matched status with a J-Score >= 70 during a channel run cycle. It can also be invoked via the SaaS API (POST /api/jobs/tailor) for the agent-mode architecture.

Inputs

InputTypeSource
resume_jsonRecord<string, unknown>profiles.resume_json column: structured JSON with experience[], education[], skills[], projects[], certifications[]
job_detailsJobDetailsConverted from jobs table row via jobRowToDetails(): title, company, location, remote_type, description, requirements[]
user_profileUserProfileConverted from profiles table row via profileRowToUserProfile(): name, years_experience, skills, resume_json

Outputs

OutputTypeDestination
contentstringTailored resume body (markdown-style formatting with sections and bullet points)
modificationsstring[]List of specific changes made and why (e.g., “Moved Python experience to top: JD lists Python as primary requirement”)
usageAIChatUsageToken counts + model + duration + generation ID: fed to cost tracking
PDF fileresume_{jobId}_{timestamp}.pdfWritten to data/resumes/, path stored in applications.tailored_resume_path
coverLetterTextstringStored in applications.cover_letter_text

Provenance

Built for projects/jobs-apply/_index as part of the automated pipeline. Tailoring quality feeds the topics/interview-rate-optimization effort. Key experiment: experiments/jobs-apply/2026-03-15-linkedin-submission-rate-ratchet: ratchet across 7 runs improved submission rates 40% to 83%. Tailoring was not the bottleneck (anti-detection was), but robustness was required.

Re-scoring gate is in shadow mode (2026-04-28): after tailoring, J-Score v2 re-scoring runs but logs the delta instead of blocking. Shadow-mode data accumulates at ~/vault/meta/resume-tailoring-shadow.jsonl with schema {timestamp, application_id, original_score, re_score, delta, resume_hash}. No production behavior change: tailored resumes are still submitted regardless of re-score.

Exit criteria (after 50 data points):

  • Median delta > -5 points: promote gate to blocking with -5 tolerance band
  • Median delta < -5 with no outcome correlation: tune prompt first, then promote
  • Median delta < -5 WITH outcome correlation: restore gate immediately at original threshold

See also: ~/vault/meta/outcome-feedback.jsonl (Phase 3 cross-reference for outcome correlation).

Related skills: skills/cover-letter-generation (parallel companion, same pipeline stage), skills/j-score-v2-matching (provides the 70+ floor that gates entry to tailoring).

Usage Notes

  • The LLM never fabricates experience. System prompt states “only rephrase and reorder existing experience, never fabricate.” The modifications[] output provides an audit trail for human review.
  • Three ATS factors: keyword density (weave job description phrases into bullets), section ordering (most relevant experience first), action verb + metric format (“Achieved X by doing Y, resulting in Z%”).
  • Contact info is excluded from the tailored output. The renderer adds it from the profile record to prevent hallucination of phone numbers or email addresses.
  • Duplicate prevention: if an application record already exists for the job, the pipeline skips retailoring and promotes directly to queued.
  • Cost: ~$0.002 per tailor+cover-letter pair. Description capped at 2000 chars, cover letter prompt at 1500 chars. 500 resumes for $1.
  • On API failure, status rolls back from tailoring to matched for retry. OpenRouterCreditsExhaustedError halts all channels immediately.

Quality Checks

  1. ATS keyword density 1.5–3% for extracted target terms. Below = under-tailored; above = keyword-stuffed.
  2. All canonical sections preserved. Experience, Education, Skills headers present post-tailor; reorder allowed, removal not.
  3. Zero content hallucinations. Every claim in the tailored resume traceable to the source resume via grep. No fabricated certifications or dates.
  4. PDF renders without errors. pdfinfo tailored.pdf exits 0; pdfimages -list shows no broken image refs.
  5. Word count 400–800. Typical 1-page = 400–500, 2-page = 700–800. Outside = re-scope.
  6. Tailored keywords highlighted in audit. score_audit.reasoning field lists the specific keywords surfaced for this role.

Visual Enrichment

MediumTypeDescription
RCMP grouped barKeyword match % before/after tailoring
FigmaFlowchartDISCOVER -> MATCH -> TAILOR -> SUBMIT -> TRACK

Outcome Feedback

Data source: ~/vault/meta/outcome-feedback.jsonl (populated by ~/Documents/jobs-apply/scripts/outcome-feedback-export.ts after each Gmail crawl cycle).

Analysis protocol:

  1. Read outcome-feedback.jsonl entries where outcome is known
  2. Group by tailoring parameters: keyword injection count, section reordering, emphasis shifts, ATS score delta
  3. Surface which tailoring strategies correlate with positive outcomes
  4. Cross-reference with shadow re-score data (~/vault/meta/resume-tailoring-shadow.jsonl) for Phase 4 gate restoration

Key metrics to track:

  • Interview rate by ATS keyword density bucket (1-2% / 2-3% / 3%+)
  • Interview rate by section reordering (yes/no)
  • Correlation between shadow re-score delta and interview outcome (Phase 4 decision data)

This data feeds the Phase 4 gate restoration decision. If low re-scores correlate with rejections, the gate was catching real issues and should be restored immediately.

Self-Improvement Cross-Reference

Pattern 3 (Metric Ratchet): ATS score optimization is metric ratcheting on keyword match rates. For the master reference on all 6 self-improvement patterns, see skills/self-improving-agent-patterns.